Dan Maharry

ASP.NET 4.0 Part 13, A New Output Caching Provider Model

by

Welcome to part 13 of my tour through ASP.NET 4.0. In this episode, we're going to look at caching. One goal for .NET as a whole is to have it use a single caching framework. ASP.NET won't make use of this global framework until .NET 5, but in the meantime, v4 does see some much needed changes in System.Web.Caching that address its more major limitations when used against high-use websites.

Up until now, ASP.NET caching has been a one trick pony with its only storage option for cached items being in-process on the server which hosts the website doing the caching. This is fine for websites of a certain size, but once they need to be scaled up across web farms or web gardens, the in-process boundary makes this built-in caching functionality no longer useful. Indeed, the need for any alternative to memory-based, process-bound caching is anathema to the built-in facilities of ASP.NET 3.5-.

Changes to caching in ASP.NET 4.0 then are not so much about performance, but geared towards creating an extensibility point for caching and introducing the notion of an output cache provider. That way, should we so choose to do so, we can define our own mechanism for both control-level output caching and page-level (i.e. caching the HTTP Response as a whole) caching.

Constraints on your Custom Provider

One of the main reasons for providing an output cache provider model is to offer a way to have items in the cache stored outside of the worker process (and therefore the AppDomain) looking after your website.
However, with that barrier crossed and caching now taking place on disk, cloud, distributed cache etc, several issues arise which mean that some constraints must necessarily be placed on any output cache provider.

  1. Absolute expiration is supported on controls and pages that use custom ouput cache providers, but sliding expiration is not. Consider the case where five web servers are all talking to the same distributed cache. There's no way to keep that sliding expiration in sync across all five machines.
  2. File dependencies are supported but custom dependencies are not. Thus if you invalidate a page, all the supporting cache items for that page will also be invalidated, but there is no support for creating a dependency between cache and other types of object using a custom CacheDependency.
  3. Static substitution, also known as doughnut-hole caching, and output cache validation are supported but only with a static delegate. This is so the caching information can survive a process restart.  When an application restarts, there will at least be a serialized value that tells us what type to get and method to use to wire the cache back up again, because it's static.

With those constraints in mind, to create your own custom cache provider, you'll need to write a class that derives from the new System.Web.Caching.OutputCacheProvider class and overrides its Get, Set, Add and Remove methods. Gunnar Peipman has an excellent post detailing a simple sample implementation for you as a starting point.

Microsoft are also slated to release two output cache providers of their own (most likely via Codeplex) post .NET 4.0 RTM. One is a disk-based caching provider and the other targets AppFabric Caching (formerly known as Velocity), a distributed caching service.

Incorporating Your Provider Into A Site

Once you've created your custom cache provider, integrating it into your site is quick and easy. First, you'll need to declare it in web.config. ASP.NET 4.0 expands <system.web>\<caching>\<outputCache> for this purpose

<configuration>
...
<system.web>
...
<caching>
<outputCache defaultProvider="AspNetInternalProvider">
<providers>
<add name="CloudCache"
type="Sample.CloudCacheProvider, SampleCacheProviders"/>
</providers>
</outputCache>
</caching>
</system.web>
</configuration>

Setting up your new caching provider to provide output caching at a control-level is now very easy. A new attribute to a control's @OutputCache directive allows you to set the provider used declaratively.

<%@OutputCache Duration="360" VaryByParam="UserName" ProviderName="CloudCache" %> 

However, should you wish to set your new provider to cache at the page level you'll need to take a different route. Cache checking at a page-level takes place in the ASP.NET processing pipeline several stages before an instance of the Page class in question has been created, so declaring your required provider in the Page's @OutputCache directive would be several steps too late to influence anything. Instead, you'll need to override a new virtual method on the HttpApplication class called GetOutputCacheProviderName in a custom class or global.asax directly to make the change.

public class Global : System.Web.HttpApplication
{
public override string GetOutputCacheProviderName(HttpContext context)
{
if (AppSettingsHelper.GetSettingAsBool("UseCloud"))
{
return "CloudCache"; // name maps to one in web.config
}
else
{
return base.GetOutputCacheProviderName(context);
}
}
}

Summary

In this post, we've looked at the new OutputCacheProvider model added to ASP.NET 4.0. We've looked at the constraints under which new cache provider classes must live and how to integrate them into our sites once written, depending on whether we need caching to work at a control or a page level.

Until next time, happy coding!

ASP.NET, Part 9: Rendering Cleaner HTML

by

Welcome to part 9 of my tour through ASP.NET 4.0. In this episode, we’ll conclude our look at the ways that ASP.NET has been tweaked in v4.0 to produce cleaner, leaner HTML that we can turn to our purpose without having to deal with some of the inconveniences of previous versions – control IDs and injected HTML we can’t control being two. More control over viewstate being a third.

ASP.NET 2.0: Only Technically Standards Compliant…

Even in 2005, the idea of producing pure HTML\CSS websites had gained quite a foothold. XHTML had been a W3C Recommendation since 2001 and CSS was evolving and being used more prevalently. Sites like CSS Zen Garden had been championing cross-browser sites and advanced layouts with this approach for a couple of years. All it required was control over the HTML generated by your site and some reasoned application of CSS.

Regrettably, ASP.NET 1.1 didn’t produce standards-compliant HTML at all, so ASP.NET 2.0 created a new setting in web.config called xhtmlConformance that would determine how its server controls were to be rendered as HTML. By default, controls would render markup compatible with the XHTML 1.0 Transitional standard (Transitional). Alternatively, the setting could be changed to have ASP.NET render markup compatible with XHTML 1.0 Strict (Strict) or as v1.1 (Legacy) has rendered it before. Websites upgraded from v1.1 to v2.0 were set to this Legacy mode.

Unfortunately, sites running in Legacy mode didn’t work with ASP.NET AJAX and while Transitional and Strict modes were technically compliant with the XHTML 1.0 standard, they could equally have been described as CSS-intolerant.

  • Menus were rendered as tables rather than lists (not only harder to work with in CSS, but semantically wrong too)
  • Several properties like border=0 or disabled=disabled were mandatorily added to various elements without a way to remove them.
  • For templated controls, you had full control over the templates themselves, but not over the outer table that surrounded the templates.

Technically correct, but not a good foundation to build standards compliant sites upon. The introduction of the ListView & DataPager controls in ASP.NET 3.5 and the out of band CSS Control Adapters release helped somewhat but it’s only because they’ve been able to go into System.Web for .NET 4.0 that Microsoft have been able to make ASP.NET (mostly) CSS-friendly. Indeed, emitting cleaner markup was one of the main goals for ASP.NET 4.0

ControlRenderingCompatibilityVersion

Like xhtmlConformance in ASP.NET 2.0, a new setting is now available in web.config to control how controls render HTML. It is called ControlRenderingCompatibilityVersion.

<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="false" targetFramework="4.0" />
<pages controlRenderingCompatibilityVersion="3.5" />
</system.web>
</configuration>

CRCV, as we’ll call it, takes one of two values. Set it to 3.5 (the default for sites upgraded from ASP.NET 3.5) and all server controls will render as they did in ASP.NET 3.5. Set it to 4.0 (the default for new web site and web application projects) and the following happens

  • xhtmlConformance is set to Strict.
  • Menus are rendered as lists rather than tables
  • Extraneous properties like border=0 are removed from the emitted markup. Even the error text on validation controls is no longer set to red.
  • The rendering of the outer table for templated controls can now be controlled with the new RenderOuterTable property.

Let’s look at these changes in detail.

Menus

Let’s consider a very simple menu control with three items.

<asp:Menu runat="server" ID="testMenu">
<Items>
<asp:MenuItem Text="Menu Item 1" NavigateUrl="#"></asp:MenuItem>
<asp:MenuItem Text="Menu Item 2" NavigateUrl="#"></asp:MenuItem>
<asp:MenuItem Text="Menu Item 3" NavigateUrl="#"></asp:MenuItem>
</Items>
</asp:Menu>

With CRCV set to 3.5, we get three CSS styles for the menu added to the HTML <head>, 53KB of script files added in by two calls to WebResource.axd (21KB for the standard script to handle postbacks, 32KB for a menu-specific script file), and the following HTML for the menu itself.

<body>
<div>
<a href="#testMenu_SkipLink">
<img alt="Skip Navigation Links" width="0" height="0" style="border-width:0px;"
src="/CleanHtml/WebResource.axd?d=y4UEb5xFzNnwyKsjxERYdw2&amp;t=634013486901596851" />
</a>
<table id="testMenu" class="testMenu_2" cellpadding="0" cellspacing="0" border="0">
<tr onmouseover="Menu_HoverStatic(this)" onmouseout="Menu_Unhover(this)" 
onkeyup="Menu_Key(this)" id="testMenun0">
<td>
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td style="white-space:nowrap;width:100%;">
<a class="testMenu_1" href="#">Menu Item 1</a>
</td>
</tr>
</table>
</td>
</tr>
<tr onmouseover="Menu_HoverStatic(this)" onmouseout="Menu_Unhover(this)" 
onkeyup="Menu_Key(this)" id="testMenun0">
<td>
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td style="white-space:nowrap;width:100%;">
<a class="testMenu_1" href="#">Menu Item 2</a>
</td>
</tr>
</table>
</td>
</tr>
<tr onmouseover="Menu_HoverStatic(this)" onmouseout="Menu_Unhover(this)" 
onkeyup="Menu_Key(this)" id="testMenun0">
<td>
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td style="white-space:nowrap;width:100%;">
<a class="testMenu_1" href="#">Menu Item 3</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
<a id="testMenu_SkipLink"></a>
</div> 
<script type="text/javascript"> 
//<![CDATA[
var testMenu_Data = new Object();
testMenu_Data.disappearAfter = 500;
testMenu_Data.horizontalOffset = 0;
testMenu_Data.verticalOffset = 0;
//]]>
</script>
</body>

With CRCV set to 4.0, we get six CSS styles added to the <head> element, a single 27KB script file downloaded to the browser (a completely refactored version of the menu script) and the following HTML for the menu.

<body>
<div>
<a href="#testMenu_SkipLink">
<img alt="Skip Navigation Links" width="0" height="0" style="border-width:0px;" 
src="/CleanHtml/WebResource.axd?d=y4UEb5xFzNnwyKsjxERYdw2&amp;t=634013486901596851" />
</a>
<div id="testMenu">
<ul class="level1">
<li><a class="level1" href="#">Menu Item 1</a></li>
<li><a class="level1" href="#">Menu Item 2</a></li>
<li><a class="level1" href="#">Menu Item 3</a></li>
</ul>
</div>
<a id="testMenu_SkipLink"></a>
</div>
<script type='text/javascript'>
new Sys.WebForms.Menu({ 
element: 'testMenu', 
disappearAfter: 500, 
orientation: 'vertical', 
tabIndex: 0, 
disabled: false });
</script>
</body>

As you can see, not only is the rendered HTML semantically correct, the overall payload is smaller as well. Indeed you can reduce it further by setting the Menu control’s IncludeStyleBlock property to false. This will remove the six styles added to the <head> element for you to include in your own CSS files. Finally, note that you can set the Menu control’s RenderingMode property to either List, Table or Default. This will override the CRCV setting and render the menu as either List or Table as you’ve set it.

Extraneous HTML

The Image control is a good example of a control that in ASP.NET 3.5 added a property to the emitted HTML that wasn’t asked for and couldn’t be overridden. Take a very simple declaration

<asp:Image runat="server" ID="imgMorgan" ImageUrl="~/morgan.jpg" />

With CRCV set to 3.5, ASP.NET sets the image’s border-width to 0px.

<img id="imgMorgan" src="morgan.jpg" style="border-width:0px;" />

With CRCV set to 4.0, there’s no sign of the style property at all.

<img id="imgMorgan" src="morgan.jpg" />

Other controls, such as Table and GridView are similarly shed of their extraneous properties.

Removing The Outer Table From Some Composite Controls

For the most part, ASP.NET controls will render the HTML you want them to, but it looks like some of the more complex, composite controls haven’t been ‘sanitized’ in the same way as more commonly used controls such as the Image or DropDownList. However, it is worth noting that some of these controls now have a new property called RenderOuterTable which does address one particular markup issue. These are

  • FormView
  • Login
  • ChangePassword
  • PasswordRecovery

Take for example the Login control.

<asp:Login runat="server" ID="loginTest" />

Whether CRCV is set to 3.5 or 4.0, the default markup is the same.

<table cellspacing="0" cellpadding="1" border="0" 
id="loginTest" style="border-collapse:collapse;">
<tr>
<td>
<table cellpadding="0" border="0">
<tr>
<td align="center" colspan="2">Log In</td>
</tr>
<tr>
<td align="right">
<label for="loginTest_UserName">User Name:</label>
</td>
<td>
<input name="loginTest$UserName" type="text" id="loginTest_UserName" />
<span id="loginTest_UserNameRequired" title="User Name is required." 
style="color:Red;visibility:hidden;">*</span>
</td>
</tr>
<tr>
<td align="right">
<label for="loginTest_Password">Password:</label>
</td>
<td>
<input name="loginTest$Password" type="password" id="loginTest_Password" />
<span id="loginTest_PasswordRequired" title="Password is required." 
style="color:Red;visibility:hidden;">*</span>
</td>
</tr>
<tr>
<td colspan="2">
<input id="loginTest_RememberMe" type="checkbox" name="loginTest$RememberMe" />
<label for="loginTest_RememberMe">Remember me next time.</label>
</td>
</tr>
<tr>
<td align="right" colspan="2">
<input type="submit" name="loginTest$LoginButton" value="Log In" 
onclick="…" id="loginTest_LoginButton" />
</td>
</tr>
</table>
</td>
</tr>
</table>

A CSS-related issue with this in earlier versions of ASP.NET is the outer table which you haven’t previously been able to remove (but which ironically was added as a wrapper in to give you better control over your styles). Set the Login control’s RenderOuterTable property to false and this will lose that table no matter what CVCR is set to.

<table cellpadding="0" border="0">
<tr>
<td align="center" colspan="2">Log In</td>
</tr>
<tr>
<td align="right">
<label for="loginTest_UserName">User Name:</label>
</td>
<td>
<input name="loginTest$UserName" type="text" id="loginTest_UserName" />
<span id="loginTest_UserNameRequired" title="User Name is required." 
style="color:Red;visibility:hidden;">*</span>
</td>
</tr>
<tr>
<td align="right">
<label for="loginTest_Password">Password:</label>
</td>
<td>
<input name="loginTest$Password" type="password" id="loginTest_Password" />
<span id="loginTest_PasswordRequired" title="Password is required." 
style="color:Red;visibility:hidden;">*</span>
</td>
</tr>
<tr>
<td colspan="2">
<input id="loginTest_RememberMe" type="checkbox" name="loginTest$RememberMe" />
<label for="loginTest_RememberMe">Remember me next time.</label>
</td>
</tr>
<tr>
<td align="right" colspan="2">
<input type="submit" name="loginTest$LoginButton" value="Log In" 
onclick="…" id="loginTest_LoginButton" />
</td>
</tr>
</table>

Not ground-shattering certainly, but definitely handy.

What We’ve Learnt

In today’s episode, we’ve learnt that Microsoft has taken the opportunity in ASP.NET 4.0 to clean up a great deal of the HTML emitted by its server controls, thus making it CSS-friendlier. A new setting in web.config called ControlRenderingCompatibilityVersion indicates whether controls should render as cleaner HTML or as they did in 3.5. We saw two example of the cleaner HTML in effect in the Menu and Image control and noted that while some of the more complex controls have not had their markup cleaned up, a subset of those now have a RenderOuterTable property which allows you to remove some of it manually.

Incidentally, for those of you with sites that you can’t upgrade to .NET 4.0, a group of enterprising souls are continuing to work on the CSS Friendly Adapters mentioned earlier. You’ll find this revitalization here on Google Code. Happy coding!

ASP.NET, Part 8: Introducing ClientIDMode

by

Welcome to part 8 of my tour through ASP.NET 4.0. In this episode, we’ll look at a new feature that directly affects two things: page size (and thus scalability and perceived performance) and the use of JavaScript APIs such jQuery. It’s called ClientIDMode.

The Problem

Consider a typical development situation. You’re building a page which inherits its structure from a master page or two. The page itself contains a custom user control which its own set of server-side controls in it.  In this particular example, let’s have a simple table.

<%@ Control Language="C#" AutoEventWireup="true" 
CodeFile="UserControl.ascx.cs" Inherits="UserControl" %>
<asp:Table runat="server" ID="tblInfo">
<asp:TableRow runat="server" ID="tblTopRow">
<asp:TableCell runat="server" ID="tdTopLeft">
<asp:Label runat="server" ID="lblChoices" Text="Choices" />
</asp:TableCell>
<asp:TableCell runat="server" ID="tdTopRight">
<asp:DropDownList runat="server" ID="ddlChoiceList">
<asp:ListItem Text="Choice One" />
<asp:ListItem Text="Choice Two" />
<asp:ListItem Text="Choice Three" />
</asp:DropDownList>
</asp:TableCell>
</asp:TableRow>
</asp:Table>

On running this page, we’ll see that the generated HTML for the table is perhaps not as succinct as we might otherwise wish. (Note that main and submain are the names of ContentPlaceholder controls in the master pages that the page containing the table is based on.)

<table id="ctl00_ctl00_main_submain_uc1Control_tblInfo" border="0"> 
<tr id="ctl00_ctl00_main_submain_uc1Control_tblTopRow"> 
<td id="ctl00_ctl00_main_submain_uc1Control_tdTopLeft">
<span id="ctl00_ctl00_main_submain_uc1Control_lblChoices">Choices</span>
</td>
<td id="ctl00_ctl00_main_submain_uc1Control_tdTopRight">
<select name="ctl00$ctl00$main$submain$uc1Control$ddlChoiceList" 
id="ctl00_ctl00_main_submain_uc1Control_ddlChoiceList"> 
<option value="Choice One">Choice One</option> 
<option value="Choice Two">Choice Two</option> 
<option value="Choice Three">Choice Three</option> 
</select>
</td> 
</tr> 
</table> 

When ASP.NET was first created, it was decided that it should autogenerate IDs for each element on the page based on the chain of their parent naming containers. This would mean that you would not be able to generate an HTML page where two elements had the same ID. As you can see above, the IDs generated in this way are neither particularly short nor semantically meaningful, but they are unique.

The length and meaning of client-side IDs didn’t matter back in 2000, but the invention and subsequent mass adoption of AJAX a few years ago has repopularised client-side scripting on the page. Scripting libraries like jQuery have only sped this tide of JavaScript up further thanks to the ease with which quite complex, cross-browser functionality can be achieved. And if there’s one thing that JavaScript uses a lot, it’s the ID of elements on a page to identify them.

The problem with ASP.NET up to v3.5 then is that there is no way to dictate the client-side ID of an HTML element and it’s hard to know exactly what the ‘AutoID’ will be for use in your scripts. A fairly complex page with several dozen controls on it will also be significantly larger than it needs to be because of AutoIDs and on high-volume sites, that means extra bandwidth used unnecessarily (and so more cost) and a perceived performance hit on the site from users with slower connections waiting for the page to download.

The Solution

It is possible to work around this issue in several ways, but with the new ClientIDMode property in ASP.NET 4.0, we’re presented with a way to instruct ASP.NET to generate element IDs in one of three ways. This new property can be set for any webform control, page, or master page. It can also be set as a property of the <system.web>/<pages> element in web.config. 

ClientIDMode has four possible values

  • AutoID has ASP.NET generate IDs as it does in v3.5 and earlier versions.
  • Static has ASP.NET use exactly the same ID given to the server control for its client-side ID.
  • Predictable has ASP.NET try to generate IDs that are guessable from looking at the structure of the page. It tries to strike a happy medium between AutoID and Static. It is also the default value for ClientIDMode in ASP.NET 4.0.
  • Inherit has ASP.NET generate a client-side ID for the page or control using the same method as its parent.

Let’s taking the example code above, and see how the <table> element is rendered in each ClientIDMode. First, let’s set the <asp:Table> control’s ClientIDMode to Predictable. As noted, this is the default in ASP.NET 4.0, so we need add no extra property. The resultant HTML looks like this:

<table id="main_submain_uc1Control_tblInfo">
<tr id="main_submain_uc1Control_tblTopRow">
<td id="main_submain_uc1Control_tdTopLeft">
<span id="main_submain_uc1Control_lblChoices">Choices</span>
</td>
<td id="main_submain_uc1Control_tdTopRight">
<select name="ctl00$ctl00$main$submain$uc1Control$ddlChoiceList" 
id="main_submain_uc1Control_ddlChoiceList">
<option value="Choice One">Choice One</option>
<option value="Choice Two">Choice Two</option>
<option value="Choice Three">Choice Three</option>
</select>
</td>
</tr>
</table>

As you can see, the resultant IDs are mostly the same as the v3.5 ones but are no longer prepended with a (random) number of ctl00_ strings. Now let’s set ClientIDMode on the table to AutoID.

<asp:Table runat="server" ID="tblInfo" ClientIDMode="AutoID">

This should generate old-style IDs for everything again as would happen in v3.5, but actually something different happens.

<table id="ctl00_ctl00_main_submain_uc1Control_tblInfo">
<tr id="main_submain_uc1Control_tblTopRow">
<td id="main_submain_uc1Control_tdTopLeft">
<span id="main_submain_uc1Control_lblChoices">Choices</span>
</td>
<td id="main_submain_uc1Control_tdTopRight">
<select name="ctl00$ctl00$main$submain$uc1Control$ddlChoiceList" 
id="main_submain_uc1Control_ddlChoiceList">
<option value="Choice One">Choice One</option>
<option value="Choice Two">Choice Two</option>
<option value="Choice Three">Choice Three</option>
</select>
</td>
</tr>
</table>

As you can see, only the ID for the table itself has been generated in the v3.5 way. Every other ID is still generated using the ‘Predictable Mode’ algorithm. Now try setting ClientIDMode to AutoID as an attribute of the @Master, @Page, or @Control directive. For example

<%@ Control Language="C#" AutoEventWireup="true" 
CodeFile="UserControl.ascx.cs" Inherits="UserControl" 
ClientIDMode="AutoID" %>

Sure enough, all the IDs for the table and its children have changed back to the v3.5 naming style as seen earlier. Try setting ClientIDMode to Static.

<%@ Control Language="C#" AutoEventWireup="true" 
CodeFile="UserControl.ascx.cs" Inherits="UserControl" 
ClientIDMode="Static" %>

Straight to the mark, the HTML element IDs now match their respective server-side control IDs exactly (but their name attributes are still generated as in v3.5-)

<table id="tblInfo">
<tr id="tblTopRow">
<td id="tdTopLeft">
<span id="lblChoices">Choices</span>
</td>
<td id="tdTopRight">
<select name="ctl00$ctl00$main$submain$uc1Control$ddlChoiceList" 
id="ddlChoiceList">
<option value="Choice One">Choice One</option>
<option value="Choice Two">Choice Two</option>
<option value="Choice Three">Choice Three</option>
</select>
</td>
</tr>
</table>

Move the ClientIDMode attribute back from the @Control directive to the <asp:Table> element and you’ll see only the <table> element return its shorter ID with the rest returning to their ‘predictable’ form.

The rule then is that ClientIDMode only changes the ID generation mode for the control on which it is set. It does not affect the ID generation mode for its child controls. The only way to change the default ClientIDMode for more than one control at a time is to set it in the @Control, @Page, or @Master directives or in web.config. The only exception to this is when setting ClientIDMode on templated list controls as we’ll see later.

If you already use jQuery in your v3.5 sites and want to upgrade them to use the v4 Framework, you would be well may need to default the ClientIDMode for the site to AutoID in web.config until you’ve checked your code works against predictable mode.

<configuration>
<system.web>
...
<pages clientIDMode="AutoID" />
...
</system.web>
</configuration>

In general however, a good approach to using ClientIDMode is to leave 'Predictable' as the default and for those few controls which are targeted by JQuery, set ClientIDMode to Static. ASP.NET will not interfere with an ID set as static so if you inadvertently set two or more identical IDs in a page, you’ll have to identify and disambiguate them yourself.

Using ClientIDRowSuffix With Templated List Controls

Consider the case where you’re binding some data into a Templated List control. For example a ListView.

<%@ Page Language="C#" AutoEventWireup="true" 
CodeFile="DataBoundControls.aspx.cs" Inherits="DataBoundControls" %>
...
<body>
<form id="form1" runat="server">
<div>
<asp:ListView runat="server" ID="lvwDemo">
<LayoutTemplate>
<table>
<asp:PlaceHolder runat="server" ID="itemPlaceholder" />
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>
<asp:Label runat="server" ID="lblItemName" Text='<%#Eval("Name") %>' />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
</div>
</form>
</body>
</html>

And in the code-behind, a simple List is bound to the ListView.

protected void Page_Load(object sender, EventArgs e)
{
List<Item> items = new List<Item> {
new Item { Id = 11, Name = "Cat" },
new Item { Id = 12, Name = "Dog" },
new Item { Id = 13, Name = "Dragon" }
};
lvwDemo.DataSource = items;
lvwDemo.DataBind();
}

With all defaults left on, a ClientIDMode of ‘Predictable’ produces the following HTML for the table templated in the ListView.

<table>
<tr><td><span id="lvwDemo_lblItemName_0">Cat</span></td></tr>
<tr><td><span id="lvwDemo_lblItemName_1">Dog</span></td></tr>
<tr><td><span id="lvwDemo_lblItemName_2">Dragon</span></td></tr>
</table>

Note that an auto-incremented number is used to make the id for the span unique. You can replace that auto-incremented number with something more meaningful using the new ClientIDRowSuffix property on the ListView. The defines the data field (typically the primary key) whose value is suffixed to the row ID to make it unique. If we set it to Id on the ListView…

<asp:ListView runat="server" ID="lvwDemo" ClientIDRowSuffix="Id">

the Id for each Item object in the list is used in the span ids.

<table>
<tr><td><span id="lvwDemo_lblItemName_11">Cat</span></td></tr>
<tr><td><span id="lvwDemo_lblItemName_12">Dog</span></td></tr>
<tr><td><span id="lvwDemo_lblItemName_13">Dragon</span></td></tr>
</table>

We could set it to Name instead of Id, as long as we can guarantee that Name is a unique field and get the following HTML.

<table>
<tr><td><span id="lvwDemo_lblItemName_Cat">Cat</span></td></tr>
<tr><td><span id="lvwDemo_lblItemName_Dog">Dog</span></td></tr>
<tr><td><span id="lvwDemo_lblItemName_Dragon">Dragon</span></td></tr>
</table>

ClientIDRowSuffix actually takes a string[], so you can set multiple fields to this property should you wish. FOr example,

<asp:ListView runat="server" ID="lvwDemo" ClientIDRowSuffix="Id, Name">

will generate the following.

<table>
<tr><td><span id="lvwDemo_lblItemName_11_Cat">Cat</span></td></tr>
<tr><td><span id="lvwDemo_lblItemName_12_Dog">Dog</span></td></tr>
<tr><td><span id="lvwDemo_lblItemName_13_Dragon">Dragon</span></td></tr>
</table>

A second new property – ClientIDRowSuffixDataKeys – will return the values used to generate the client ID for a given row.

ClientIDRowSuffix and Static\AutoID Modes Don’t Mix

Be aware that ClientIDRowSuffix has no effect on either AutoID or Static ID modes and also that server-side controls placed within templates DO inherit the ClientIDMode of their parent control – in this case, the ListView or GridView in whose templates they are placed. For example, should you set ClientIDMode to Static on the ListView control in the current example…

<asp:ListView runat="server" ID="lvwDemo" ClientIDRowSuffix="Id" ClientIDMode="Static">

every span id will remain identical despite the presence of ClientIDRowSuffix.

<table>
<tr><td><span id="lblItemName">Cat</span></td></tr>
<tr><td><span id="lblItemName">Dog</span></td></tr>
<tr><td><span id="lblItemName">Dragon</span></td></tr>
</table>

If you set it AutoID rather than Static, this is not a problem as AutoIDs are unique by design but there’s no influence on them via ClientIDRowSuffix either.

<table>
<tr><td><span id="lvwDemo_ctrl0_lblItemName">Cat</span></td></tr>
<tr><td><span id="lvwDemo_ctrl1_lblItemName">Dog</span></td></tr>
<tr><td><span id="lvwDemo_ctrl2_lblItemName">Dragon</span></td></tr>
</table>

You could set the ClientIDMode to Predictable on the Label control in your template

<asp:ListView runat="server" ID="lvwDemo" ClientIdRowSuffix="Id" ClientIDMode="Static">
...
<ItemTemplate>
<tr>
<td>
<asp:Label ClientIDMode="Predictable"
runat="server" ID="lblItemName" Text='<%#Eval("Name") %>' />
</td>
</tr>
</ItemTemplate>
</asp:ListView>

but then you’ll notice that the span ids become slightly less predictable than you might like.

<table>
<tr><td><span id="ctrl0_lblItemName_11">Cat</span></td></tr>
<tr><td><span id="ctrl1_lblItemName_12">Dog</span></td></tr>
<tr><td><span id="ctrl2_lblItemName_13">Dragon</span></td></tr>
</table>

What We’ve Learnt

In this episode, we’ve seen that the new ClientIDMode property gives us more control over the client-side IDs generated by server-side controls. This helps both with keeping the overall size of the page down and for referencing the correct client-side IDs in our Javascript code. We’ve seen when ClientIDMode affects or does not affect the child controls of the one on which it is set and also how ClientIDRowSuffix augments predictable mode when it comes to generating IDs for controls within the templates of a databound list control. We also noted that ClientIDRowSuffixDataKeys returns the values of the keys set in ClientIDRowSuffix used to generate a unique ID for a given row.

ASP.NET, Part 7: A New Viewstate Opt-in Model

by

Welcome to part 7 of my tour through ASP.NET 4.0. Today we’re going to look at what in hindsight was one of the larger design mistakes that Microsoft made when creating ASP.NET – The View State Opt-Out Model for Web Forms – and how they’ve approached a solution.

[Updated Feb 24: Added link to more in-depth tutorial on View State at end of article].

HTTP is Stateless

One of the first things you learn as a web developer is that HTTP is a stateless protocol. Requests sent over HTTP are independent of any that have gone before and retain no information about the user sending them or the state of the page (where the state is defined as the property values of the controls that make up the page’s control hierarchy.)

The issue then is how to retain such information between requests and, more significantly in this discussion, between postbacks on the same page. The standard solutions are to

  • Put it in session state
  • Put it in the URL
  • Put it in a cookie

Enter View State

Back in 2000, View State was created by Microsoft so we fledgling web developers didn’t have to worry about persisting this state information onto the next page. It was just another great feature whereby this information was base64-encoded on the server and stored in a hidden form element on the page named __VIEWSTATE. When the page was posted back, View State would be sent back with it and hey presto, instant state.

Ten years later, the benefit of using View State is still apparent but, as any ASP.NET MVC developer will tell you, it is not a necessary part of a web page. (To whit, MVC doesn’t use View State at all.) There are two main issues with View State

  1. All pages take a performance hit as the server must deal with decrypting, reading, rebuilding and re-encrypting view state with each post back of a page to the server. The exact process is described here. Pages which dynamically add controls to the page may also cause exceptions when the server cannot match the control tree in the view state to that on the page.
  2. The value in the __VIEWSTATE hidden form field can grow quite large quite quickly. Without you realising it, it could add several dozen kilobytes to your page resulting in slower transmission times to your users and back to the server when it is sent back to the server in the HTTP POST headers.

The solution to both these problems for Web Forms developers is to make sure that only the controls which need their state persisted across postbacks are included in View State and herein lies the main problem with View State that is being addressed in ASP.NET 4.0.

Opt-Out Mode vs. Opt-In Mode

From ASP.NET v1.0 to v3.5 SP1, there has only been an opt-out model for View State on a Web Forms page. This means that View State is switched on for every control on every page by default, unless you opt to disable it by setting the Page or Control’s EnableViewState property to false. The problem is that once EnableViewState is set to false for a Page for example, it is also set to false for every child control of that page, and there is no facility to override that further down the tree and have View State switched on for a control on that page.

Consider the scenario where I have several dozen form controls on a page and wish to maintain view state for just one DropDownList. I cannot set EnableViewState to false for the whole Page and then set it to True for the DropDownList. Instead I must leave EnableViewState set to true on the page and explicitly set it to false for every other control on the page as long as it isn’t a parent control for the DropDownList.  

Which isn’t too great.

Fortunately, Microsoft has heeded our cries and added an opt-in model for View State in ASP.NET 4.0. Addressing the same scenario using the opt-in model, I can indeed switch off View State for the whole Page and then re-enable it explicitly on the one DropDownList I need to maintain state for. However, I do it with a new property called ViewStateMode rather than EnableViewState.

Introducing ViewStateMode

The switch between opt-out and opt-in models is made using the new ViewStateMode property. This has three possible values.

  • Enabled (the default for a Page) : sets the page \ control to the old school opt-out mode
  • Disabled : sets the page \ control to use the new opt-in mode.
  • Inherit (the default for a user control) : sets the control to inherit the setting from its parent page \ control.

It can be set both code-behind and declaratively in markup as an attribute of a control or of the @Page directive.

<%@Page ... ViewStateMode="Disabled" %>

Let’s take an example. In the code below, we have a normal WebForms page with EnableViewState set to true by default.

<%@ Page Language="C#" 
AutoEventWireup="true" CodeFile="ViewStateDemo.aspx.cs"
Inherits="ViewStateDemo" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>View State Demo</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:PlaceHolder runat="server" ID="plhOuter">
<asp:Label runat="server" ID="lblOne" Text="My Initial Value" /><br />
<asp:Panel runat="server" ID="pnlInner">
<asp:Label runat="server" ID="lblTwo" Text="My Initial Value" />
</asp:Panel>
</asp:PlaceHolder>
<br />
<asp:Button runat="server" ID="btnGo" Text="View State Is Off" />
</div>
</form>
</body>
</html>

In the code-behind for this page, we re-set the Text properties for the two Labels and Button if the page has not posted back.

protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
lblOne.Text = "My set value";
lblTwo.Text = "My set value";
btnGo.Text = "View State Is On";
}
}

When it loads, the page will load and the new Text values displayed. With View State enabled, those text values will stay the same when you click the button and post the page back to the server.

With ViewState On Across the board

Now set ViewStateMode to Disabled for on the plhOuter PlaceHolder control.

<asp:PlaceHolder runat="server" ID="plhOuter" ViewStateMode="Disabled">

Now when you run the page and click the button, you’ll see that the two Labels lose their value as it is not persisted in View State. However, the Button control, which is outside the PlaceHolder still uses View State.

View State Off On Outer Placeholder

So far, this is nothing new. We could replace the ViewStateMode property with EnableViewState set to false and get the same result. However, we could not then also set ViewStateMode to Enabled on the pnlInner Panel control and restore ViewState to the lblTwo Label control.

<asp:Panel runat="server" ID="pnlInner" ViewStateMode="Enabled">

Now run the page and click the button again to confirm that View State is now on for lblTwo. If you did try and replace ViewStateMode property with EnableViewState set to false on the plhOuter control now and hit the button, you’ll see that EnableViewState overrides ViewStateMode and produces the results in the screenshot above rather than the one below.

ViewState On For Inner Panel

The ViewStateMode property can be set on any control or on the Page itself and not just invisible container controls as we’ve seen so far. Move ViewStateMode=”Disabled” from the plhOuter control to the @Page directive.

<%@ Page ViewStateMode="Disabled" Language="C#" 
AutoEventWireup="true" CodeFile="ViewStateDemo.aspx.cs"
Inherits="ViewStateDemo" %>

Run the page again and you’ll see how the Button control now also loses state across a post back while lblTwo continues to retain it as ViewStateMode is still enabled on it.

ViewStateMode Disabled At Page Level

Remember : View State is not Control State

While this demo adequately shows you how ViewStateMode is inherited through the Control hierarchy of a page, it would have failed had I tried to use TextBox or DropDownList controls instead of Labels with the code-behind setting their selected value or text. The DropDownList would have retained its SelectedIndex and the TextBox its Text whether View State was enabled or not because those particular properties are stored in control state. Control state is designed for storing a control's essential data (such as a TextBox's text) that must be available on postback to enable the control to function even when view state has been disabled. Thus control state is always maintained even if EnableViewState is set to false and ViewStateMode is set to Disabled.

What We’ve Learnt

In today’s episode, we’ve seen that ASP.NET 4.0 presents a new opt-in mode for View State on a page via the new ViewStateMode property. However, we’ve also seen that setting the EnableViewState property to false will disable View State for any child control or page regardless of whether one has had ViewStateMode set to Enabled on it. Thus for any control’s state to be saved in a page’s View State, the following must be true.

  • EnableViewState must be set to true for both the Page and the Control (which is the default setting)
  • ViewStateMode must be set to Enabled on the Control.

We also noted that the setting of ViewStateMode has no influence over the workings of a Control’s Control State.

For a really good, in-depth look at how View State works, you can look at both of these:

Happy Coding!

ASP.NET 4.0, Part 6: New Page Directive Attributes

by

Welcome to part 6 of my tour through ASP.NET 4.0. Over the next few posts we’ll be looking at some of the changes made to the standard webforms page. For today, we’ll start at the top and look at the new Page directive attributes in this release in anticipation of some future posts.

The default @Page directive added to the top of every new webform generated by Visual Studio 2010 has not changed from that in VS2008. It still has the standard four attributes.

<%@ Page Language="C#" AutoEventWireup="true" 
CodeFile="Default.aspx.cs" Inherits="_Default" %>

Indeed, all 42 (yes, 42!) attributes from .NET 2.0 have been kept for backwards compatibility as you might expect.  You might be surprised how many there are: some present per-page overrides to the <pages> element in your web.config files, others how the page should operate, and others how the code-behind the page fits into the code hierarchy of your site. A brief explanation of them all can be found here.

ASP.NET 4.0 brings with it six new attributes for the @Page directive, as follows:

  • ClientIDMode sets the method ASP.NET will use to render the IDs for every element on the page. Possible values are AutoID (the default), Predictable, Static and Inherit. We’ll investigate how they each work in a forthcoming post.
  • ClientTarget overrides the automatic browser capabilities detection we saw in part 5 and let’s you specify explicitly what browser capabilities the ASP.NET engine should target when rendering the page.
  • MetaDescription sets the Page’s new MetaDescription property which is then rendered in the HTML <meta> description tag.
  • MetaKeywords sets the Page’s new MetaKeywords property which is then rendered in the HTML <meta> keywords tag.
  • TargetSchema identifies the schema that validates content on the page when being designed. It’s purpose is similar to a property of the same name in .NET 1.1. Note that TargetSchema is for descriptive purposes only; no actual validation is performed, and the attribute is ignored by the parser.
  • ViewStateMode specifies whether the page’s ViewState is controlled by an opt-in or an opt-out (the default) model. As with ClientIDMode, we’ll investigate this further in part 7.

In tomorrow’s post, we’ll look in more depth at the new options for controlling ViewState on a page. Happy coding!