Saturday, January 26, 2013

Fix No source available problem when debug ASP.Net internal code

Sometimes you need to debug internals of ASP.Net. It helps to understand some important aspects of functioning of the framework and improves the understanding of how it works in general. Here is the good article which explains how to configure Visual Studio for debugging .Net code (which of course includes ASP.Net code): Configuring Visual Studio to Debug .NET Framework Source Code. This article was written for VS2008, in VS2010 and VS2012 steps are almost the same, bug there are several improvements. First of all there is new “Enable .NET Framework source stepping” setting:

image

This msdn article How to: Debug .NET Framework Source says that we need to check it in order to be able to step into the .Net code. However on practice I was able to debug .Net code even with having it unchecked.

Another improvement is having preconfigured Microsoft Symbols Servers option in Tools > Debugging > Symbols:

image

So now you don’t need to add and configure it manually.

Ok, so you did all described steps, tried to debug your ASP.Net application, set breakpoint e.g. in Page_Load method of your page and in Call Stack window can see that symbols for whole stack are loaded properly:

image

Also if you will check Symbol Load Information from the context menu:

image

you will see that symbols are successfully loaded:

image

However if you will try to double click on any row in Call Stack window you may see the message that source code is unavailable:

image

It may be confusing because you did everything according to instructions which you found in many blog posts and forums. However there is one more important thing which you should do. In order to debug ASP.Net code use real IIS web server in the project settings instead of Visual Studio Development Server:

image

After you will change project settings and click on some .Net internal call from Call Stack you will see the .Net source code:

image

Hope that this information will help you during the debugging.

Saturday, January 19, 2013

ASP.Net Checkbox.Checked is set to default when use disabled attribute or difference between Enabled property and disabled attribute

Update 2013-01-27: the problem described in this post is reproducible in IE9. In other browsers when you add disabled attribute to the Checkbox, user still may change the value and bug is not reproduced.

Some time ago I faced with interesting behavior of ASP.Net Checkbox control, which confused me a bit. And I decided to dig into the problem in order to understand why it works like this. Consider the following example:

   1: <form id="form1" runat="server">
   2: <div>
   3:     <asp:CheckBox runat="server" ID="chk" Checked="True" disabled="disabled"
   4:         Text="Checkbox"/><br/>
   5:     <asp:Button runat="server" ID="btn" Text="Postback"/>
   6: </div>
   7: </form>

So we have 2 controls: one checkbox and one button. Button is needed only for testing postbacks, so we won’t pay attention to it. Checkbox has 2 important properties: one is Checked=”True” and another disabled=”disabled” attribute. I.e. we want to stay it checked and not allow users to change the value. When the page with these controls is requested first time (IsPostback = false), it looks like this:

image

Everything looks correct. But if we will click button (IsPostback = true), then it will look like this:

image

I.e. Checked property became false after postback. Why it happens?

The problem is in disabled=”disabled” attribute. According to html spec controls which have disabled attribute are “unsuccessful” controls (see here), which values are not included to the form post data. Ok, let’s remember it and try to run the following test:

   1: <form id="form1" runat="server">
   2: <div>
   3:     <asp:CheckBox runat="server" ID="chk" Checked="True" Enabled="False"
   4:         Text="Checkbox"/><br/>
   5:     <asp:Button runat="server" ID="btn" Text="Postback"/>
   6: </div>
   7: </form>

It is almost the same, but except disabled attribute we used server property Enabled=”false”. In this case when we will request page first time we will get the following result:

image

Checkbox is still checked and user still can’t change its value, but now label is not greyed out. If we will click Postback button now, picture will remain the same:

image

In this case Checked property was not reset to the false. The interesting things will start when you will check generated html in second example:

   1: <input name="chk" disabled="disabled" id="chk" type="checkbox" CHECKED="checked"/>

So under the scene, input control anyway has disabled attribute, but its checked property is not lost when we use server property. The question why it happens interested me, so I made some investigation. Such cases is very good chance to download ASP.Net sources and debug it.

Let’s check what happens in control’s lifecycle in both examples. As you probably know when page is requested first time ASP.Net creates class in "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\webapplication\" folder – inheritor of the class which is used as codebehind for your page. Default values are set in this class: it has overridden FrameworkInitialize function (which is called right after constructor in lifecycle) which calls @__BuildControlchk function:

FrameworkInitialize > @__BuildControlTree > ... > @__BuildControlchk

Function @__BuildControlchk looks like this:

   1: private global::System.Web.UI.WebControls.CheckBox @__BuildControlchk() {
   2:     global::System.Web.UI.WebControls.CheckBox @__ctrl;
   3:     @__ctrl = new global::System.Web.UI.WebControls.CheckBox();
   4:     this.chk = @__ctrl;
   5:     @__ctrl.ApplyStyleSheetSkin(this);
   6:     @__ctrl.ID = "chk";
   7:     @__ctrl.Checked = true;
   8:     ((System.Web.UI.IAttributeAccessor)(@__ctrl)).SetAttribute("disabled", "disabled");
   9:     @__ctrl.Text = "Checkbox";
  10:     @__ctrl.SetTraceData(typeof(Microsoft.VisualStudio.Web.PageInspector.Runtime.WebForms.TraceData), new Microsoft.VisualStudio.Web.PageInspector.Runtime.WebForms.TraceData(395, 90, false));
  11:     return @__ctrl;
  12: }

As you can see Checked property is set to true on line 7. And disabled attribute is added on line 8. Now is turn to investigate the code of Checkbox control. You can do it in some decompiler or you can enable Microsoft Symbols Server in Tools > Options > Debugging > Symbols in VS and enable debug of .Net code:

- Uncheck "Enable Just My Code (Managed only)"
- Check "Enable source server support"
- Uncheck "Require source files to exactly match the original version"

First of all we are interesting in OnPreRender method:

   1: protected internal override void OnPreRender(EventArgs e) {
   2:     base.OnPreRender(e);
   3:     ...
   4:     if (!SaveCheckedViewState(autoPostBack)) {
   5:         ViewState.SetItemDirty("Checked", false);
   6:  
   7:         ... 
   8:     }
   9: }

I removed the code which is not important for our investigation. On line 7 it calls SaveCheckedViewState function in order to determine should save Checked property into ViewState or not:

   1: /// <devdoc> 
   2: ///   Controls whether the Checked property is saved in ViewState.
   3: ///   This is used for optimizing the size of the view state.
   4: /// </devdoc>
   5: private bool SaveCheckedViewState(bool autoPostBack) { 
   6:     // Must be saved when
   7:     // 1. There is a registered event handler for SelectedIndexChanged 
   8:     // 2. Control is not enabled or visible, because the browser's
   9:     //    post data will not include this control 
  10:     // 3. The instance is a derived instance, which might be overriding
  11:     //    the OnSelectedIndexChanged method
  12:     //    This is a bit hacky, since we have to cover all the four derived
  13:     //    classes we have... 
  14:     // 4. AutoPostBack is true and Adapter doesn't support JavaScript
  15:     //    For CheckBoxes to behave the same on mobile devices
  16:     //    that simulate AutoPostBack by rendering a command button, we need to save
  17:     //    state 
  18:     //
  19:  
  20:     if ((Events[EventCheckedChanged] != null) || 
  21:         (IsEnabled == false) ||
  22:         (Visible == false) || 
  23:         (autoPostBack == true && ((Page != null) && !Page.ClientSupportsJavaScript))) {
  24:  
  25:         return true;
  26:     } 
  27:  
  28:     Type t = this.GetType(); 
  29:     if ((t == typeof(CheckBox)) || (t == typeof(RadioButton))) { 
  30:         return false;
  31:     } 
  32:  
  33:     return true;
  34: }

I leaved comments here from the source code of the Checkbox control which say that it is done for optimizing the size of the view state. Debugger showed that in first example (when we use disabled attribute) SaveCheckedViewState returns false from line 30. IsEnabled in the first example is true, because we didn’t set it to true explicitly. It means that in Checkbox.OnPreRender method the following call will be made:

   1: ViewState.SetItemDirty("Checked", false);

It actually marks property as not dirty, but in result it means that it won’t be included into the view state. Check the code of StateBag.SaveViewState:

   1: internal object SaveViewState()
   2: {
   3:     ArrayList list = null;
   4:     if (this.bag.Count != 0)
   5:     {
   6:         IDictionaryEnumerator enumerator =
   7: this.bag.GetEnumerator();
   8:         while (enumerator.MoveNext())
   9:         {
  10:             StateItem item = (StateItem) enumerator.Value;
  11:             if (item.IsDirty)
  12:             {
  13:                 if (list == null)
  14:                 {
  15:                     list = new ArrayList();
  16:                 }
  17:                 list.Add(new IndexedString((string) enumerator.Key));
  18:                 list.Add(item.Value);
  19:             }
  20:         }
  21:     }
  22:     return list;
  23: }

As you can see only items which have IsDirty = true are stored into the list of view state objects (lines 11-19). For us all of this means that in first example Checked property is not stored to the ViewState of the control for optimizing the size of the view state, according to the comments. Checked property is retrieved from the view state:

   1: public virtual bool Checked { 
   2:     get {
   3:         object b = ViewState["Checked"]; 
   4:         return((b == null) ? false : (bool)b); 
   5:     }
   6:     set { 
   7:         ViewState["Checked"] = value;
   8:     }
   9: }

As in first example it is removed from the view state, every call to get_Checked will return false. This is the reason of losing the Checked property in first example. During postback Checkbox.LoadPostData method is called:

   1: protected virtual bool LoadPostData(string postDataKey,
   2: NameValueCollection postCollection) { 
   3:     bool dataChanged = false;
   4:  
   5:     string post = postCollection[postDataKey];
   6:  
   7:     bool isChecked = (!String.IsNullOrEmpty(post));
   8:  
   9:     if (isChecked) {
  10:         ValidateEvent(postDataKey); 
  11:     } 
  12:  
  13:     dataChanged = (isChecked != Checked); 
  14:     Checked = isChecked;
  15:  
  16:     return dataChanged;
  17: }

As you can see it gets value from the post values collection and if it is empty, isChecked will be false. This is what happens in first example, because as I wrote above controls with disabled attribute are not posted. This method is called after OnInit and before OnLoad method, so e.g. in OnInit method Checked is still true (because we have default value initialization in temporary ASP.Net class – see above), but in OnLoad it will be false because of this LoadPostData call.

Now what happens in second example when we use Enabled = false property? In this case SaveCheckedViewState method returns true which means that Checked will be stored in view state. But as we saw above when we have Enabled = false, disabled attribute is also added. It is added in Checkbox.RenderInputTag method:

   1: internal virtual void RenderInputTag(HtmlTextWriter writer,
   2: string clientID, string onClick) {
   3:     ...
   4:     if (!IsEnabled && SupportsDisabledAttribute) {
   5:         writer.AddAttribute(HtmlTextWriterAttribute.Disabled, "disabled"); 
   6:     ...
   7: }

So in this case value also is not included to the post data, but why then Checked doesn’t become false in Checkbox.LoadPostData method like in first example?

The answer is because Checkbox.LoadPostData method is not called at all in second example. Let’s check the code of the Page.ProcessPostData method which calls LoadPostData methods from child controls:

   1: private void ProcessPostData(NameValueCollection postData, bool fBeforeLoad) {
   2:     ...
   3:     foreach (string controlID in _controlsRequiringPostBack) { 
   4:         ...
   5:         bool changed = consumer.LoadPostData(controlID, postCollection);
   6:         ...
   7:     }
   8:     ...
   9: }

I.e. page calls this method only for controls from _controlsRequiringPostBack collection. Let’s check Checkbox.OnPreRender method again:

   1: protected internal override void OnPreRender(EventArgs e) {
   2:     ...
   3:     if (Page != null && IsEnabled) {
   4:         Page.RegisterRequiresPostBack(this);
   5:         ...
   6: }

I.e. checkbox is added to _controlsRequiringPostBack collection only when it is Enabled. If Enabled = false, postback data is not even loaded for it.

The summary of the investigation is that Enabled=”false” will cause disabled=”disalbed” attribute, but not vice versa. I hope that this article will help you to understand the difference between Checkbox.IsEnabled property and disabled attribute. It also shows that sometimes it is useful to download ASP.Net code and investigate it in the debugger.