Friday, May 30, 2008

Access DataItem declaratively using data binding syntax

Sometimes there is a need to access a DataItem object of a data-bound server control. It usually happens when you want to display the value of the bound object as a whole instead of its property.

Standard data binding syntax assumes using a Bind(string name) or Eval(string name) methods in the context of data binding expression where name is a name of a property of the DataItem object. But what if the data item if of type that does not have properties like System.String for examples?

In this case there is another method that you can use: GetDataItem(). It will return to you the current instance of the DataItem object that you can use.

Syntax example:

<%# GetDataItem() %>

That sort of syntax can be used even for DataSource properties in complex nested databound template controls.

Last-Modified HTTP header on ASP.NET page

"Last-Modified" HTTP header is an important part of every web page since it helps browsers to cache pages' content locally and thus save HTML traffic. It is also used by internet search engines in order to determine the relevance of the pages' content and thus improves pages' indexing. By default a standard ASP.NET page does not include a "Last-Modified" header in the output HTML.

One of the possible explanation for such behavior is that an ASPX page is considered dynamic by nature meaning that its content might be updated every time the page is requested. It is true in many cases for data-driven and interactive pages but most of the dynamic web sites also contain pages that are updated relatively rare or static.

So when does it make sense to add a "Last-Modified" header to the page? First candidates usually are data-driven pages that display information from some sort of content storage systems (databases, files, etc.) and that information changes but not very frequently so there is a possibility that the same content may be viewed many times. Other apparent candidates are static pages even if they are data-driven but their content does not change for the lifetime.

What are the pages that do not need the "Last-Modified" header? Obvious candidates are interactive pages and pages that displays user session related data.

ASP.NET provides two methods to set the "Last-Modified" header on the page. Both of them belong to the HttpCachePolicy class and are accessible through the Cache property of the Response object:

Response.Cache.SetLastModified(DateTime date);
Response.Cache.SetLastModifiedFromFileDependencies();

The first method takes a DateTime parameter that allows you to apply some algorithm for calculating the Last-Modified date of the page, for instance based on information from a database. The second method will calculate the date stamp automatically based for instance on the assembly build date. For the second method you can also include additional file dependencies in date calculation using

Response.AddFileDependancy(string filename);

For instance if a page displays a content of a static XML file you could include that XML file in the dependency and the "Last-Modified" header's value would be updated based on that file's "Date Modified" attribute.

Friday, May 23, 2008

ScriptManager vs. ToolkitScriptManager

Introduction
ScriptManager is a special ASP.NET server control that should be placed on a page before you can use any of AJAX.NET enabled controls. The same rule is true for the AJAX Control Toolkit controls: they all require a ScriptManager on the page. While AJAX Control Toolkit controls work perfectly fine with the standard ASP.NET ScritpManager the Toolkit includes its own version of the ScriptManager called ToolkitScriptManager that inherits from ScriptManager and is meant to improve some of the ScriptManager's behaviors in particular how it renders out behavior JS scripts. Let's examine how using a ToolkitScriptManager changes a web page's appearance.

Experiment
As a testing example I have created a very simple page consisting of a single Accordion control from the AJAX Control Toolkit library.
<head runat="server">
    <title>Untitled Page</title>
    <style type="text/css">
        .accHead { border:1px solid #445566;font-size:larger;background-color:#aaa;}
        .accHeadSel { border:1px solid #445566;font-size:larger;background-color:#444;color:#fff;}
        .accCont { border:1px solid #ccc;padding:5px;}
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <ajax:ToolkitScriptManager ID="tsm" runat="server"></ajax:ToolkitScriptManager>
    <%-- <asp:ScriptManager ID="sm" runat="server"></asp:ScriptManager>--%>
    <div>
<ajax:Accordion
    ID="MyAccordion"
    runat="Server"
    SelectedIndex="0"
    HeaderCssClass="accHead"
    HeaderSelectedCssClass="accHeadSel"
    ContentCssClass="accCont"
    AutoSize="None"
    FadeTransitions="true"
    TransitionDuration="250"
    FramesPerSecond="40"
    RequireOpenedPane="false"
    SuppressHeaderPostbacks="true">
    <Panes>
        <ajax:AccordionPane ID="AccordionPane1" runat="server"
            HeaderCssClass="accHead"
            ContentCssClass="accCont">
            <Header>Pane Header 1</Header>
            <Content>Content 1</Content>
        </ajax:AccordionPane>        
        <ajax:AccordionPane ID="AccordionPane2" runat="server"
            HeaderCssClass="accHead"
            ContentCssClass="accCont">
            <Header>Pane Header 2</Header>
            <Content>Content 2</Content>
        </ajax:AccordionPane>        
        <ajax:AccordionPane ID="AccordionPane3" runat="server"
            HeaderCssClass="accHead"
            ContentCssClass="accCont">
            <Header>Pane Header 3</Header>
            <Content>Content 3</Content>
        </ajax:AccordionPane>        
    </Panes>            
    <HeaderTemplate>List of Panes</HeaderTemplate>
    <ContentTemplate></ContentTemplate>
</ajax:Accordion>
    </div>
    </form>
</body>

You may have noticed that right after the form tag there are ScriptManager (SM) control and ToolkitScriptManager (TSM) control on the page but one of them is commented out. Next I run a page from VS 2008 two times: first using the SM and second using TSM, and compare the results.

HTML output and traffic
Let's compare the HTML output of two page versions. See a WinMerge screen shot below that shows the SM version in the left pane and the TSM version in the right pane.



First difference you see is that TSM adds its own hidden field on the page (top right pane) and the next and more important difference is that TSM renders one script reference on the page instead of the five ones that SM does.

Now if we examine the page's traffic with the FireBug we'll see how that changes the traffic: first graph refers to the page using the SM control and the second one when the TSM's at work.




Analyzing the graphs it's easy to see that the TSM does reduce a number of browser's round-trips by combining multiple script references into a single one. This advantage will become more attractive as more AJAX-enabled server controls will be placed on a web page that support script combining. However it is not necessarily true that it will always reduce the page loading time. As you can see when TSM returns a combined script to the page it takes some time to perform a work on the server. Correspondingly the amount of work and consequentially the response time directly depend on the number of scripts to combine (that is a number of AJAX server controls) and the size of the scripts to process (white spaces removal and compressing).

So there is no single recommendation that it's always preferable to use TSM instead of SM. In many cases SM version of a page may work faster then a TSM version of the page. Since it's not that difficult to alter a page for both cases I'd advise to test and compare both versions in your particular scenario before making a decision which one to choose.

Thursday, May 22, 2008

ToolkitScriptManager and Web Deployment Project

Problem description
If you use AJAX Control Toolkit in your project you probably use ToolkitScriptManager from the Toolkit instead of standard ASP.NET ScriptManager class. Now if you want to deploy your web application using Web Deployment Project (WDP) for VS2008 then you will find out very quickly that your AJAX enabled application features don't work. The reason for that is there is a JavaScript error on a page saying that the root JavaScript object for AJAX Control Toolkit is not defined on the page and all the subsequent scripts relying on that object have failed. I am not reproducing the exact message here because it may vary in different situations.

Background
You have tested your web application many times in your Dev environments and everything worked just fine. So what you've changed is you used a Web Deployment Project to build and deploy your web application and you presumably wanted to take advantage of a famous precompile and merge feature of the WDP.

Explanation
Now the problem is that the missing JavaScript on the page is rendered by a ToolkitScriptManager object that generate a link to a JavaScript code using a ScriptResource handler which is in its turn supposed to retrieve the code embedded into assembly. Because WDP changes the names and locations of the assemblies ToolitScriptManager was unable to resolve the assembly name and could not generate the link so your page does not have that JavaScript reference any more.

Workaround
Use standard ASP.NET ScriptManager instead of ToolkitScriptManager and everything will work again.

What's next?
I am in a process of investigating whether a ToolkitScriptManager brings any advantages comparing to a new ScriptManager 3.5 in terms of more efficient HTML rendering and will post here my findings as soon as possible.

Also if you are interested in employing a ToolkitScriptManager together with the Web Deployment Project I invite you to vote for this bug on Codeplex.

#if DEBUG in ASP.NET 2.0 code behind

If you use an ASP.NET web site in your solution (another alternative is WAP - web application project) you may have noticed that there is no more compiler options in Configuration Manager that would have allowed you to add compiler constants like DEBUG. So the question pops up whether it's still possible to use very convenient way of excluding debug code from the production version by using #if preprocessor directive. Luckily it turns out that it's still supported and now it's related to a value of a "debug" attribute of a "compilation" element of the Web.config file. If you surround a part of your code with #if DEBUG ... #endif it will be included when debug="true" and excluded when debug="false".

Tuesday, May 6, 2008

Upgrading your WCSF solution from June 2007 release to Feb 2008 release

You need to start from reading the original WCSF Wiki post describing how to perform an upgrade from the previous June 2007 release to the latest Feb 2008 release.

In a nutshell there are a few steps that you will need to do:
  1. Install .NET Framework 3.5
  2. Remove WCSF June 2007 release
  3. Upgrade your Guidance Automation software from any earlier release to the Feb 2008 for VS 2008 release
  4. Install WCSF Feb 2008 release
  5. Load your solution in VS2008 and make all the necessary changes.
That's a plan. Most if those steps are straight-forward and don't require additional explanations especially with the help of the original WCSF upgrade instructions. However there are a few tricky moments I'd like to focus on.
  1. When you remove the older Guidance Automation remember the following two rules:
    • First remove all the Guidance Automation packages installed on your machine;
    • Remove first the Guidance Automation Toolkit for VS and then remove Guidance Automation Extensions.

    If you happened to see an exception saying that some Guidance Automation Packages are still not removed while trying to uninstall the Guidance Automation Extensions you need to remove references to those packages first in order to be able to successfully uninstall Guidance Automation Extensions.
    Go to the Documents and Settings\..\Application Data\Microsoft\Recipe Framework folder and find a RecipeFramework*.xml file. Open it with a text editor and remove the content between GuidancePackages open and closing tags. Now you'll be able to uninstall Guidance Automation Extensions.

  2. After you installed WCSF Feb 2008 you'll need to enable it for your solution. It's done through the Visual Studio Guidance Automation Manager plug-in. The only thing that I'd recommend is to change a content of a vwd.webinfo file in the root fo your web site first because it differs for WCSF Feb 2008 comparing to WCSF July 2007. Make sure that the content of the vwd.webinfo looks like it's described here.

  3. Now you're ready to modify your code and here are the list of mandatory changes that you'll need to do in order to make your application working:

    • Remove the references to the previous WCSF assemblies from all your projects (basically they are foundation modules, business modules and web sites) and add the references to the WCSF Feb 2008 assemblies. Pay attention that there is no more such an assembly as Microsoft.Practices.ObjectBuilder.WCSFExtensions as it is merged into Microsoft.Practices.CompositeWeb.ObjectBuilder.

    • Modify all the [ModuleName]ModuleInitializer.cs files: in the ModuleInitializer Load method, use the concrete class CompositionContainer instead of ICompositionContainer.

    • Modify all the Pages code behind .cs files: a Presenter property now has both getter and setter methods and a setter method pattern is a bit different as shown below:
         [CreateNew]
      public [ViewName]Presenter Presenter
      {
      get { return presenter; }
      set
      {
      if (value == null)
      throw new ArgumentNullException("value");

      presenter = value;
      presenter.View = this;
      }
      }

      And also a page base class now should derive from a WCSF base class as shown below:
      public partial class [Module]_[View] : Microsoft.Practices.CompositeWeb.Web.UI.Page, I[View]

    • Optionally if you used a solution from WCSF_Contrib project for User controls then you'll have to alter those controls the same way as your pages.
So that's basically all. It does not look hard and should not take much of your time. But if you happened to have troubles feel free to post a comment here and I'll try to answer.

Thursday, May 1, 2008

Adding a Web Site to a WCSF solution

Whether you are adding a new Web Site to an existing WCSF solution or trying to enable WCSF for an existing solution make sure that a "vwd.webinfo" file is located in the root of a Web Site folder. The Web Site won't be recognized by WCSF's recipes unless there is a "vwd.webinfo". If you already have a Web Site in your solution just copy the file to the root folder of a new web site. If you can't find the file just create the one. Here is how it's content should look like:

<?xml version="1.0" encoding="UTF-8"?>
<visualwebdeveloper>
<globals>
<userproperties iswebproject="true">
</globals>
</visualwebdeveloper>