I've been very busy lately and unfortunately didn't have enough time to stop here to write about things I have been working on. There are other subjects that I'd like to talk about but, for now, I'll share my findings with asp.net user controls dynamically added to the page, difficulties, limitations and solutions.

I'm working on a Ajax based web site that basically contains a content area in the right, where I need to put the dynamic content, based on the user selection on the left menu. The ongoing work can be seen here.

The easier way to do this is to create an asp.net UserControl for the content of each page and to dynamically add each control to the page based on the user selection. I also added an UpdateProgress panel for better user experience. However, here is where the problems start. First off, you cannot simply instantiate an asp.net UserControl through its constructor and add it to the page. That won't work. Instead, you'll need to use the factory method Page.LoadControl(), providing it the virtual path to your control so that it returns the instance for you. Eg.:

   1:  Control c = this.LoadControl("~/MyUserControl.ascx");   // Instantiate the user control
   2:  this.ContentArea.Controls.Add(c);    // Add it to the placeholder

Here, we're instantiating the control and adding it to the page. So far, so good. But, what happens if your user control needs to execute some javascript function upon loading? Even if you explicitly put <script> tags in your user control markup, it won't be executed at this time, so the solution is to use RegisterStartupScript to execute the client code:

   1:  ClientScript.RegisterStartupScript(typeof(this), "some script key", "<script type="text/javascript">alert('hello world!');</script>");

or, if you're using Ajax, use the ScriptManager static method:

   1:  ScriptManager.RegisterStartupScript(this, typeof(this), "some script key", "<script type="text/javascript">alert('hello world!');</script>", false);

Put this and your user control will show up smoothly. If you don't like the fact that the screen may remain unresponsive for some time with Ajax, you can use UpdateProgress to show a "Loading" text, for example, while the content is being loaded. This is fairly easy. Check out this video for further information.

Okay, so now, what happens if we need to fire an event inside the UserControl in response to a user click on an image, for example? Right, the click event will fire a page postback and then the event handling code will be fired in the code behind as expected. Right? Definitely no! Since our user controls are being added dynamically to the page, they will not be there anymore after the postback. This means that ASP.Net will not find the control and, therefore, no event will be fired.

Let me refresh some facts about ASP.Net postback functionality before continuing… Whenever you click a server control in the page, javascript __doPostBack function is called (except for Button and ImageButton, that use a different method – form post). The __doPostBack function takes two arguments, eventTarget and eventArgument.  The eventTarget contains the ID of the control that causes the postback and the eventArgument contains any additional data associated with the control. Upon page reload, ASP.Net checks both Request.Params["__EVENTTARGET"] and Request.Params["__EVENTARGUMENT"] parameters for any postback data. If it finds it, it looks for the control ID and method specified in the __EVENTTARGET var and calls the specified method, passing the __EVENTARGUMENT value as a parameter.

Said that and knowing that our dynamic user control won't be present in the page during the postback, it is easy to conclude that ASP.Net won't find the control for the ID in the __EVENTTARGET parameter and, consequently, nothing will happen.

Okay, so we need to re-add our user control to the page during the postback, so that ASP.Net will be able to find it and fire its event handler. In order to accomplish that, we can use the page ViewState to store the name of the control currently in the page, so that we can re-add it in the page OnLoad event.

   1:  protected override void OnLoad(EventArgs e)
   2:  {
   3:      base.OnLoad(e);
   4:      
   5:   
   6:      if (!this.IsPostBack)
   7:      {
   8:          // If this is not a postback, set our the initial content page
   9:          ChangeSection("~/InitialControl.ascx");
  10:      }
  11:      else
  12:      {
  13:          // Else, retrieve the CurrentControl value from the ViewState
  14:          string currentSection = ViewState["CurrentControl"] as string;
  15:          Control c = ChangeSection(currentSection);    // Use the control name to add it to the page
  16:   
  17:          if (c is Eventos_EventosRealizados)
  18:          {
  19:              ((Eventos_EventosRealizados)c).UpdateCallback = delCallback;
  20:          }
  21:      }
  22:  }
  23:   
  24:  public Control ChangeSection(string _sControl)
  25:  {
  26:      Control c = this.LoadControl(_sControl);
  27:      
  28:      // Put a 3 seconds delay so that we can see the magic happen -- remove in the final version
  29:      System.Threading.Thread.Sleep(3000);
  30:   
  31:      this.ContentArea.Controls.Add(c);    // Add the control to the page
  32:   
  33:      // Important: Set its ID or the event might not be fired,
  34:      // since ASP.Net uses the control ID to find it
  35:      c.ID = "ContentUserControl";
  36:   
  37:      ViewState["CurrentControl"] = _sControl;  // store the control name in the ViewState
  38:      
  39:      // Execute some javascript routine to refresh the screen (if needed)
  40:      ScriptManager.RegisterStartupScript(this, typeof(this), "some script key", "<script type="text/javascript">refreshPage();</script>", false);
  41:   
  42:      return c;
  43:  }

So these are just some basic guidelines for handling user controls dynamically. If you need further info, asp.net official website is always a great place to start off and watch with some videos.

Till next time!

 

I just found out that there is an effort at Microsoft together with the jQuery team in order to add intellisense support for this framework in Visual Studio 2008. We all know that the javascript intellisense support in VS has always been almost none, but now it seems that the guys at Microsoft are changing the way and starting to support it progressively, starting by including it in its ASP.NET MVC package and adding intellisense support for interaction with the AJAX framework.

I've been trying to enable this functionality today and found out that it just isn't worth the effort, at least for the latest jQuery version (1.3.2). Basically, you need to first off install a patch to enable the functionality. Then, you'll need a -vsdocs.js file, which is just an API descriptor containing all the documentation for the framework. It is not actually a js file. You can find more detailed information here. Then, JScript IntelliSense will parse all the included js files and check for errors. If it thinks that one of your included script files isn't good, intellisense just won't work. The problem is that it complains about the actual jQuery file not being well formed:

"Warning 15 Error updating JScript IntelliSense: C:\….\javascript\jquery-1.3.js: Object doesn't support this property or method @ 2069:1"

Just to shorten the story, you'll get to spend some time trying to make it to work, probably without success and then will give up miserably (my case). The fact is that it just isn't worth all the work, while you still have a decent online documentation to help. 

In the end, you'll have to calm down and wait a little bit while Visual Studio 2010 is still beta since jQuery will be shipped with VS2010 and it will include built-in robust intellisense and documentation (not from a downloaded patch). This is good news for us all! In the meantime, let's wait for the final release :)

 

[Update 07/17/2009]

Just ran into a great post about this exactly same subject at learninjquery.com which was an eye-opener to me. It is actually very simple to make it work if you know exactly what has to be done. And what has to be done is just to add the /// reference comment to the start of your javascript file and everything works seamlessly. No need to include the -vsdocs in the actual html file at all! Of course that if you have different scenarios like custom controls, dynamically generated javascript, etc., this may become painful again, but for the regular usage, this post at learningjquery.com will set you will all that is needed to be ready to go in minutes.

 

Lately I found a very simple and yet consistent way to track the last update made to a Sql Server database table. You can easily store the modification date and time through a table trigger that can set a specific column of the updated row(s) with the GETDATE() function.

In order to do that, create a new trigger for your table and use the Inserted virtual table to obtain the ID of the entry, in order to update it. We'll use here a LastModified column, which will store a datetime:

CREATE TRIGGER last_modification_timestamp
ON target_table
AFTER INSERT, UPDATE
AS
DECLARE @colID INT
SELECT @colID = (SELECT ID FROM Inserted)
UPDATE target_table SET LastModified = GETDATE() WHERE ID = @colID

So, we're basically retrieving the column ID (primary key) from the affected row and using it to update the LastModified column with the current date and time.

This way, you can enforce this policy and guarantee that you'll always get the correct date/time in that column, since the trigger will be automatically executed just after each INSERT and DELETE.

Hope this helps!

 

When working with the Entity Framework, you've got to be extremely careful when exchanging Entities between different ObjectContexts. You probably know that if you want to modify an Entity which is already added to the database, you have to first attach it to the context before modifying it, so that EF can keep track of the changes you made to that instance and save them to the database later. There is a good reference page at msdn about attaching related objects.

Attach assumes your Entity has not been changed while it was not being tracked by the ObjectContext. Otherwise, call ApplyPropertyChanges to attach and apply the changes.

Yesterday, I lost almost my entire day trying to translate this exception I was getting in ObjectContext.Attach():

"System.InvalidOperationException : An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key."

EF complains that the Entity you're trying to add actually has an EntityKey identical to one that is already in the database. Okay, this seems obvious to me, since this is the actual purpose of Attach – start tracking changes objects that already exist in the database.

After an entire afternoon searching for an answer, I came accross some pages with other issues related to Attach that some people had, as well some solutions for them.

Today I re-read the exception message and the answer just popped on my face: "I'm already tracking the object you're trying to Attach, dumbass!". In fact, you cannot Attach an object to an ObjectContext if that object somehow has been already cached by the ObjectStateManager.

Let me exemplify:

   1:  public void UpdateUserBooks(User _user)
   2:  {
   3:      using (DataEntities db = new DataEntities())
   4:      {
   5:          Book userBook = (from b in db.Book.Include("Users")
   6:                          select b).FirstOrDefault();
   7:          
   8:          db.Attach(_user);    // throws InvalidOperationException
   9:          userBook.Users.Add(_user);
  10:          db.SaveChanges();
  11:      }
  12:  }

 

What is happening? Your userBook query returns all the Books from the database AND their related Users (the Include was there for a reason). When you try to attach your User after_user  after the query has been run, the ObjectContext already has _user under its hood, so trying to Attach it will throw an InvalidOperationException.
The solution is very simple: Always attach all your Entities before doing any operation/query in your ObjectContext. This way, you avoid any double-tracking request. If the ObjectContext needs your Entity later, it will retrieve the instance you attached before and you're good to go! Follow the fixed code:

 

   1:  public void UpdateUserBooks(User _user)
   2:  {
   3:      using (DataEntities db = new DataEntities())
   4:      {
   5:          db.Attach(_user);    // works fine!
   6:          
   7:          Book userBook = (from b in db.Book.Include("Users")
   8:                          select b).FirstOrDefault();
   9:          
  10:          userBook.Users.Add(_user);
  11:          db.SaveChanges();
  12:      }
  13:  }