When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article

ASP ASP.NET ASP FAQs Message Board Feedback
 
Print this Page!
Published: Wednesday, February 25, 2009

Using Expression Builders in ASP.NET

By Scott Mitchell


Introduction


ASP.NET offers a variety of ways to inject the results of a server-side expression (such as DateTime.Now.ToString()) into the rendered markup of an ASP.NET page. The most common way is to add a Label Web control to the page and then from the Page_Load event handler (or some other suitable event handler) assign the value to display to the Label's Text property. If you ever created web applications using ASP.NET's predecessor, classic ASP, or if you are familiar with Microsoft's ASP.NET MVC, then you know that another way to inject server-side information is to add <%= expression %> to the declarative content like so:

The current time is: <%= DateTime.Now.ToString() %>

The <%= expression %> syntax is translated into Response.Write(expression), injecting the value of expression into the page's rendered output. Because <%= expression %> is translated into (essentially) a Response.Write these statements cannot be used to set the values of Web control properties. In other words, you cannot have markup like the following:

<asp:Label runat="server" id="CurrentTime" Text="<%= DateTime.Now.ToString() %>" />

An alternate way to display server-side information is to assign it to a Web control property directly from the declarative markup using an expression builder. An expression builder is denoted using the syntax <%$ expression %> (note the $ after <%). The expression cannot be an arbitrary snippet of code as with <%= ... %>, but instead is limited to what expression builders the website is configured to use. Moreover, expression builders must be assigned to a Web control property; they cannot appear in any which place in the declarative markup like <%= expression %>.

ASP.NET ships with three built-in expression builder classes that let you declaratively access the values in the configuration's AppSettings collection, the ConnectionStrings collection, and in the website's resources (typically defined in the App_LocalResources and App_GlobalResources folders). With a little bit of code you can create your own expression builders. This article provides an overview of how expression builders work and shows how to create your own expression builders. Read on to learn more!

- continued -

A Primer on How Declarative Markup Is Translated Into Code


When an ASP.NET page is visited the declarative markup in the page is converted into source code and compiled into an assembly that derives from the page's code-behind class. In short, this step turns the declarative markup - the static HTML content and Web control markup - into a C# or Visual Basic class file. This auto-generated class's primary responsibility is to create the page's control hierarchy, which involves creating LiteralControls for each block of static HTML, creating objects that represent each Web control on the page, and adding these controls to the Page object's Controls collection. You can find these auto-generated classes in the %WINDOWS%\Microsoft.NET\Framework\version\Temporary ASP.NET Files folder.

Consider the following simple page, which has a Web Form (<form runat="server">), inside which there is a mix of static HTML and Web controls, namely a TextBox, Button, and Label.

<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
   ...
</head>
<body>
   <form id="form1" runat="server">
      <p>
         Please enter your name:
         <asp:TextBox ID="NameInput" runat="server" />
         <br />
         <asp:Button ID="btnSubmit" runat="server" Text="Click Me!" onclick="btnSubmit_Click" />
      </p>
      <p>
         <asp:Label ID="Results" runat="server" />
      </p>
   </form>
</body>
</html>

When this page is visited for the first time (or for the first time after the page's content has changed) the declarative markup is transformed into a class file that constructs the page's control hierarchy based on the contents in the declarative markup. The control hierarchy construction is kick started by the auto-generated class's FrameworkInitialize method, which contains a call to a method named BuildControlTree. @__BuildControlTree is where the magic happens. @__BuildControlTree starts by creating a LiteralControl that contains the static HTML markup at the start of the document (<html>, <head>, <body>, etc.). Next, it generates the control hierarchy for the Web Form. It completes by creating a LiteralControl that contains the static HTML markup at the end of the document (</body>, </html>, etc.).

private void @__BuildControlTree(codedemo_aspx @__ctrl) {
   // Add the static markup at the top of the page
   @__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("<html xmlns="http://www.w3.org/1999/xhtml" ><head>...</head>"));
   
   // Create the Web Form's control hierarchy and add it to the page
   global::System.Web.UI.HtmlControls.HtmlForm @__ctrl2;
   @__ctrl2 = this.@__BuildControlform1();
   @__parser.AddParsedSubObject(@__ctrl2);

   
   // Add the static markup at the bottom of the page
   @__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("</body></html>"));
}

The @__BuildControlform1 method creates the control hierarchy for the Web Form, creating and adding Web control objects for each Web control defined in the Web Form. This method also sets the properties for the Web Form that are defined in the declarative markup (the ID, for instance).

private global::System.Web.UI.HtmlControls.HtmlForm @__BuildControlform1() {
   // Create the HtmlForm object and set its ID property
   @__ctrl = new global::System.Web.UI.HtmlControls.HtmlForm();
   @__ctrl.ID = "form1";
   
   
   // Add the NameInput TextBox
   global::System.Web.UI.WebControls.TextBox @__ctrl1;
   @__ctrl1 = this.@__BuildControlNameInput();
   @__parser.AddParsedSubObject(@__ctrl1);
   
   // Add the Submit Button
   global::System.Web.UI.WebControls.Button @__ctrl2;
   @__ctrl2 = this.@__BuildControlbtnSubmit();
   @__parser.AddParsedSubObject(@__ctrl2);

   
   // Add the Results Label
   global::System.Web.UI.WebControls.Label @__ctrl3;
   @__ctrl3 = this.@__BuildControlResults();
   @__parser.AddParsedSubObject(@__ctrl3);
   
   return @__ctrl;
}

The methods that create the corresponding Web control objects follow a similar pattern: they create the Web control in question and set those properties that were defined in the declarative markup. If the Web control has children controls, the logic recurses into additional method calls to add those children Web controls. Here's the code for the @__BuildControlbtnSubmit, which generates the object and sets the properties for the Button Web control. As you can see, those Button properties defined explicitly in the markup - ID and Text - are assigned to "btnSubmit" and "Click Me!", respectively.

private global::System.Web.UI.WebControls.Button @__BuildControlbtnSubmit() {
   global::System.Web.UI.WebControls.Button @__ctrl;
   
   @__ctrl = new global::System.Web.UI.WebControls.Button();
   @__ctrl.ID = "btnSubmit";
   @__ctrl.Text = "Click Me!";

   @__ctrl.Click += new System.EventHandler(this.btnSubmit_Click);

   return @__ctrl;
}

As I noted in the introduction, one way to inject server-side information into the rendered markup is to use <%= expression %>. Adding such content writes expression directly to the output stream alongside the other literal content on the page. That is why you cannot assign a Web control value using the <%= expression %> syntax.

Enter Expression Builders


Starting with ASP.NET 2.0 it is possible to have more control over the value assigned to the Web controls' properties by the auto-generated class. Instead of only being able to assign a static value to a property via the declarative syntax (as in <asp:Label ... Text="Hello!" />), it is possible to use an expression builder to assign the property a dynamic value. With an expression builder you use the syntax <%$ expression %> to assign a Web control property a dynamic value (as in <asp:Label ... Text="<%$ expression %>" />).

When the runtime converts the declarative markup to the auto-generated class file, it parses the expression builder syntax to determine what expression builder class is responsible for generating the corresponding expression. Such a class must extend the ExpressionBuilder class, which spells out the default functionality for expression builders. Specifically, the corresponding expression builder class's GetCodeExpression method is called and passed information about the Web control property the expression builder is being bound to, the value of expression in the <%$ expression %> syntax, and other information. The expression builder class is responsible for generating a CodeExpression instance that will be used to assign the Web control's property value. (The CodeExpression class is part of the .NET Framework's CodeDom library, which allows for developers to write code that generates code.)

To better understand how expression builders work, let's review the AppSettingsExpressionBuilder class, which is one of three expression builder classes that are built into the .NET Framework. As its name implies, the AppSettingsExpressionBuilder class is used to return values from the AppSettings section defined in the application's configuration (Web.config). For example, imagine that you have a Web.config file with the following <appSettings> section:

<configuration>
   <appSettings>
      <add key="CopyrightNotice" value="Copyright Me!" />
   </appSettings>

   ...
</configuration>

You could use an expression builder like so to display this value in a Label control on an ASP.NET page:

<div id="footer">
   <asp:Label runat="server" id="Copyright" Text="<%$ AppSettings:CopyrightNotice %>" />
</div>

When this page was first visited and the declarative markup converted into an auto-generated class that constructs the control hierarchy, the runtime would parse the <%$ AppSettings:CopyrightNotice %> and say, "Ah, I need to call the AppSettingsExpressionBuilder class's GetCodeExpression method in order to correctly assign the property value to this Label's Text property." The GetCodeExpression method would then be invoked. The AppSettingsExpressionBuilder class would see that the request was for the AppSettings key "CopyrightNotice". It would then return a CodeExpression instance that specified the code to execute as AppSettingsExpressionBuilder.GetAppSettings("CopyrightNotice", typeof(Label), "Text"). This code is actually injected into the auto-generated class in the method that creates the Label and sets it properties.

Consider the code that is auto-generated if the Label's Text property is assigned a static value, such as "Hello, World!" As we discussed earlier, it would look something like:

private global::System.Web.UI.WebControls.Label @__BuildControlCopyright() {
   global::System.Web.UI.WebControls.Label @__ctrl;
   
   @__ctrl = new global::System.Web.UI.WebControls.Label();
   @__ctrl.ID = "Copyright";
   @__ctrl.Text = "Hello, World!";

   return @__ctrl;
}

This is as we expect. But notice the code that gets generated when using an expression builder (namely <%$ AppSettings:CopyrightNotice %>):

private global::System.Web.UI.WebControls.Label @__BuildControlCopyright() {
   global::System.Web.UI.WebControls.Label @__ctrl;
   
   @__ctrl = new global::System.Web.UI.WebControls.Label();
   @__ctrl.ID = "Copyright";
   @__ctrl.Text = Convert.ToString(AppSettingsExpressionBuilder.GetAppSettings("CopyrightNotice", typeof(Label), "Text"));

   return @__ctrl;
}

Look at that! The actual code specified via the CodeExpression object in the AppSettingsExpressionBuilder class is echoed into the code for the auto-generated class. The AppSettingsExpressionBuilder.GetAppSettings method, as you may have guessed, gets and returns the specified AppSettings value via the ConfigurationManager.AppSettings collection. The net result is that a developer can inject server-side information into the property of a Web control in the declarative portion by using an expression builder. When the declarative markup is translated into code, the expression builder gets to inject code into this auto-generated class, such as pulling a value from the AppSettings, returning the current date and time, or any other number of tasks.

Creating a Custom Expression Builder Class


The .NET Framework ships with three built-in expression builder classes:
  • AppSettingsExpressionBuilder - retrieves a value from the AppSettings defined in the web application's configuration file.
  • ConnectionStringsExpressionBuilder - retrieves a value from the ConnectionStrings defined in the web application's configuration file.
  • ResourceExpressionBuilder - retrieves a resource value. Resources are commonly used to specify culture-specific values for localized applications.
If these built-in expression builder classes don't meet your needs you can always create your own custom expression builder classes. All expression builder class's must extend the ExpressionBuilder class, which defines the base functionality. Moreover, your class must implement, at minimum, the GetCodeExpression class, whose duty it is to return a CodeExpression object that contains the code to inject when assigning the code expression to a Web control property in the auto-generated class.

Let's create a custom expression builder class named SessionExpressionBuilder that allows page developers to declaratively assign Session variables to properties of Web controls in the declarative markup. The idea here is that a page developer could display the value of a Session variable named, say, "Foo", in a Label control using the following declarative markup:

<asp:Label runat="server" ... Text="<%$ Session:Foo %>" />

To start, we need to create a class that extends ExpressionBuilder (which is found in the System.Web.Compilation namespace). Moreover, we need to override the GetCodeExpression method:

using System;
using System.Web;
using System.Web.Compilation;
using System.CodeDom;
using System.ComponentModel;

namespace skmExpressionBuilders
{
   public class SessionExpressionBuilder : ExpressionBuilder
   {
      public override CodeExpression GetCodeExpression(System.Web.UI.BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
      {
         // TODO
      }
   }

}

The GetCodeExpression method needs to return a CodeExpression object, which can be one of any number of code generation tasks. For instance, the CodeExpression could perform a binary operation (such as addition), generate primitive expression (such as a literal string or number), or invoke a method, among other options. We want the CodeExpression to invoke a method so that the auto-generated class calls a method in our custom expression builder class when assigning the expression builder value to the Web control property. For this reason, the GetCodeExpression returns a CodeMethodInvokeExpression object that calls GetRequestedValue, a static method in the custom expression builder class:

public class SessionExpressionBuilder : ExpressionBuilder
{
   public override CodeExpression GetCodeExpression(System.Web.UI.BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
   {
      CodeExpression[] inputParams = new CodeExpression[] { new CodePrimitiveExpression(entry.Expression.Trim()),
                                                       new CodeTypeOfExpression(entry.DeclaringType),
                                                       new CodePrimitiveExpression(entry.PropertyInfo.Name) };

      // Return a CodeMethodInvokeExpression that will invoke the GetRequestedValue method using the specified input parameters
      return new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(this.GetType()),
                                  "GetRequestedValue",
                                  inputParams);
   }

   ...
}

The CodeMethodInvokeExpression constructor accepts:

  • The type that the method being called belongs to (this.GetType(), which returns the type of the custom expression builder class, SessionExpressionBuilder)
  • The method name to invoke ("GetRequestedValue"), and
  • The input parameters to pass into this method. We are passing in three parameters: the expression in the <%$ expression %> syntax, which is accessible via the Expression property of the BoundPropertyEntry object passed into the GetCodeExpression method; the type of the Web control whose property is being assigned; and the name of the property being assigned.
The GetRequestedValue method, shown below, checks to ensure that the Session variable of interest exists. Next, it compares the type of the property being assigned and the type of the Session variable being assigned to it. If there is a type mismatch it ensures that the Session variable type can be converted to the Web control property type and then performs the conversion. If the two types match, it simply returns the Session variable value.

public class SessionExpressionBuilder : ExpressionBuilder
{
   ...

   public static object GetRequestedValue(string key, Type targetType, string propertyName)
   {
      // First make sure that the Session collection will be available
      if (HttpContext.Current == null)
         return null;

      // Get the value from Session
      object value = HttpContext.Current.Session[key];

      // Make sure that the specified Session variable exists
      if (value == null)
         throw new InvalidOperationException(string.Format("Session variable '{0}' not found.", key));

      // Convert the Session variable if its type does not match up with the Web control property type
      if (targetType != null)
      {
         PropertyDescriptor propDesc = TypeDescriptor.GetProperties(targetType)[propertyName];
         if (propDesc != null && propDesc.PropertyType != value.GetType())
         {
            // Type mismatch - make sure that the Session variable value can be converted to the Web control property type
            if (propDesc.Converter.CanConvertFrom(value.GetType()) == false)
               throw new InvalidOperationException(string.Format("Session variable '{0}' cannot be converted to type {1}.", key, propDesc.PropertyType.ToString()));
            else
               return propDesc.Converter.ConvertFrom(value);
         }
      }

      // If we reach here, no type mismatch - return the value
      return value;
   }
}

Before you can use the custom expression builder in your project you need to register it in Web.config. Go to the <compilation> element and add the following:

<configuration>
   <system.web>
      <compilation ...>
         <expressionBuilders>
            <add expressionPrefix="prefix" type="type"/>
            ...
         </expressionBuilders>
      </compilation>

      ...
   </system.web>
   
   ...
</configuration>

Replace prefix with the expression builder prefix you want to use, such as "Session". Replace type with the type name of the custom expression builder class. If you added it to the App_Code folder in a Web Site Project the type is simply the name of the class, SessionExpressionBuilder. If you are using a Web Application Project or have the expression builder class in a separate Class Library project, the type should include the namespace and assembly, like so: "namespace.SessionExpressionBuilder, assembly".

With this configuration information in place you can now use the expression builder in an ASP.NET page. For example, imagine that you had a page with a Label and you wanted to set the Label's Text and Font-Size properties based on Session variables "Message" and "MessageFontSize", respectively. You could use declarative markup like the following to achieve this:

<asp:Label ID="WelcomeMessage" runat="server" Text="<%$ Session:Message %>" Font-Bold="True"
           Font-Size="<%$ Session:MessageFontSize %>" />

When this page is visited and the declarative markup is translated into an auto-generated class, the class assigns the Label's Text and Font.Size properties a value returned by the SessionExpressionBuilder.GetRequestedValue method. The code that gets auto-generated looks like the following:

@__ctrl.Text = System.Convert.ToString(SessionExpressionBuilder.GetRequestedValue("Message", typeof(System.Web.UI.WebControls.Label), "Text"), System.Globalization.CultureInfo.CurrentCulture);
@__ctrl.Font.Size = ((System.Web.UI.WebControls.FontUnit)(SessionExpressionBuilder.GetRequestedValue("MessageFontSize", typeof(System.Web.UI.WebControls.FontInfo), "Size")));

The SessionExpressionBuilder.GetRequestedValue method is grabbing the passed-in Session variable ("Message" and "MessageFontSize") from Session state and returning its value, which is then assigned to the Label's Text and Font.Size properties.

Assuming that you have specified the values for these two Session variables previously in the user's session, the Label will incorporate these Session variables' values. The screen shot below shows a demo that's available for download at the end of this article. Prior to visiting this page the following code was executed:

Session["Message"] = "Welcome to My Website (FROM SESSION!)";
Session["MessageFontSize"] = FontUnit.Point(48);

The result is that the Label's Text property is set to "Welcome to My Website (FROM SESSION!)" and its Font-Size property set to 48pt.

The Label's Text and Font-Size properties are set by the SessionExpressionBuilder.

One Custom Expression Builder Library To Rule Them All!


It would have been nice if the .NET Framework had shipped with more expression builders. To address that shortcoming, I've decided to create my own library of custom expression builder classes that you are welcome to download and use in your projects. This library, named skmExtensionBuilders, is available for download at the end of this article, along with a demo website that shows these expression builders in action. (And, if you couldn't guess, it includes the SessionExpressionBuilder we just examined.) To learn more about this library check out: skmExpressionBuilders - A Suite of Custom Expression Builder Classes.

Until then... Happy Programming!

  • By Scott Mitchell


    Attachments:


  • Download skmExpressionBuilders (a suite of free custom expression builder classes)
  • Further Reading


  • Five Undiscovered Features on ASP.NET 2.0
  • Express Yourself With Custom Expression Builders
  • Expression Builders in ASP.NET 2.0
  • The Code Expression Builder
  • ExpressionBuilder Class Technical Docs
  • skmExpressionBuilders - A Suite of Custom Expression Builder Classes


  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article