Express Yourself With Custom Expression Builders

One of the hidden gems in ASP.NET 2.0 is the new expression syntax. For example, to display the value of a setting in the AppSettings section of your web.config, you can do this:

<asp:Label Text="<%$ AppSettings:AnotherSetting %>"
    ID="setting" 
    runat="server" />

Notice that the value of the Text property of the Label control is set to an expression that is similar to the DataBinding syntax (<%#), but instead of a pound sign (#) it uses a dollar sign ($).

Expressions are distinguished by the expression prefix. In the above example, the prefix is AppSettings.  The following is a short list of built in expression prefixes you can use. I am not sure if there are more:

  • Resources
  • ConnectionStrings
  • AppSettings

But like most things in ASP.NET, this system is extensible, allowing you to easily build your own custom expressions. In this blog post, I’ll walk you through building a query string expression builder. This will allow you to display a query string value like so:

<asp:Label Text="<%$ QueryString:SomeParamName %>"
    ID="setting" 
    runat="server" />

The first step is to create a class that inherits from System.Web.Compilation.ExpressionBuilder. Be sure not to confuse this with System.Web.Configuration.ExpressionBuilder

using System.Web.Compilation;

[ExpressionPrefix("QueryString")]
public class QueryStringExpressionBuilder : ExpressionBuilder
{
  //Implementation goes here...
}

ExpressionBuilder is an abstract class with a single abstract method to implement. This method returns an instance of CodeExpression which is part of the System.CodeDom namespace. For those not familiar with CodeDom, it’s short for Code Document Object Model. It is an API for automatic code generation. The CodeExpression class is an abstract representation of code that gets executed each time your custom expression is evaluated.

You’ll probably use something similar to the following implementation 99% of the time though (sorry for the ugly formatting, but this pretty much mimics the implementation in the MSDN documentation).

public override CodeExpression GetCodeExpression(
    BoundPropertyEntry entry
    , object parsedData
    , ExpressionBuilderContext context)
{
  Type type = entry.DeclaringType;
  PropertyDescriptor descriptor = 
    TypeDescriptor.GetProperties(type)
      [entry.PropertyInfo.Name];
  CodeExpression[] expressionArray = 
    new CodeExpression[3];
  expressionArray[0] = new 
    CodePrimitiveExpression(entry.Expression.Trim());
  expressionArray[1] = new 
    CodeTypeOfExpression(type);
  expressionArray[2] = new 
    CodePrimitiveExpression(entry.Name);

  return new CodeCastExpression(descriptor.PropertyType
    , new CodeMethodInvokeExpression(
        new CodeTypeReferenceExpression(GetType())
        , "GetEvalData"
       , expressionArray));
}

So what exactly is happening in this method? It is effectively generating code. In particular, it generates a call to a static method named GetEvalData which needs to be defined in this class. The return value of this method is then cast to the type returned by descriptor.PropertyType, which is why you see the CodeCastExpression wrapping the other code expressions.

The arguments passed to GetEvalData are represented by the CodeExpression array, expressionArray. The first argument is the expression to evaluate (this is the the part after the prefix). The second argument is the target type. This is the type of the class in which the expression is being evaluated. In our case, this would be the type System.Web.UI.WebControls.Label as we are using this expression within a Label control. The final argument is entry. This is the name of the property being set using the expression. In our case, this would be the Text property of the Label.

You could really build any sort of code tree within this method, but as I said before, most of the time, you will follow a similar pattern as this. In fact, I would probably put this method in some sort of abstract base class and then make sure to define the static GetEvalData method in your inheriting class.

Note, if you choose to move this method into an abstract base class as I described, you can’t make GetEvalData an abstract method in that class because we generated a call to a static method.

You could consider changing the above method to build a call to an instance method, but then you the generate code would have to create the instance everytime your expression is evaluated. It would not have access to an instance of the expression builder automatically. The choice is yours.

Here is the GetEvalData method we need to add to QueryStringExpressionBuilder.

public static object GetEvalData(string expression
    , Type target, string entry)
{
    if (HttpContext.Current == null 
      || HttpContext.Current.Request == null)
        return string.Empty;

    return HttpContext.Current
      .Request.QueryString[expression];
}

With the code for the builder completed, you simply need to add an entry within the compilation section under the system.web section of web.config like so:

<system.web>
  <compilation debug="true">
    <expressionBuilders>
      <add expressionPrefix="QueryString" 
        type="NS.QueryStringExpressionBuilder, AssemblyName"/>
    </expressionBuilders>
  </compilation>
</system.web>

This maps your custom expression class to the expression via its prefix.

In the MSDN examples, they tell you to drop your expression class file into the App_Code directory. This works when you are using the Website Project model. Fortunately, you can also use custom expressions with Web Application Projects. Simply compile your builder into an assembly and make sure to specify the AssemblyName as part of the type attribute when declaring your expression builder.

If you are using the WebSite project model and the App_Code directory, you should leave off the AssemblyName portion of the type.

What others have said

Requesting Gravatar... DotNetKicks.com Nov 30, 2006 1:34 AM
# Express Yourself With Custom Expression Builders
You've been kicked (a good thing) - Trackback from DotNetKicks.com
Requesting Gravatar... Christopher Steen Dec 01, 2006 12:26 PM
# Link Listing - December 1, 2006
Attend 3 Webcasts, get free software [Via: gduthie ] Video: Debugging ASP.NET AJAX Applications with...
Requesting Gravatar... Kyle Dec 02, 2006 2:12 AM
# re: Express Yourself With Custom Expression Builders
GetEvilData? Why would I want any devil-spawn data?
Requesting Gravatar... Jason Haley Dec 02, 2006 9:01 AM
# Interesting Finds: Week after Thanksgiving 5
Requesting Gravatar... Lazy Coder Dec 02, 2006 11:12 PM
# Custom Expression Builders
&quot;One of the hidden gems in ASP.NET 2.0 is the new expression syntax.<br />For example, to display the value...
Requesting Gravatar... Boris Yeltsin Dec 03, 2006 8:47 PM
# re: Express Yourself With Custom Expression Builders
Why would I use this over say <%= AppSettings("MySetting") %> or similar. I don't see the value or the saving? What am I missing? :)
Requesting Gravatar... Haacked Dec 04, 2006 12:23 AM
# re: Express Yourself With Custom Expression Builders
<%= translates to a Response.Write statement, which means it is not part of the control hierarchy.

Suppose you want to set a TextBox to the value of an AppSetting. You couldn't do that with <%=

Not only that, if you use Masterpages and dynamically loaded controls, <%= can cause problems. For example, when placed in a user control dynamically loaded by a master page.
Requesting Gravatar... Shanku Niyogi Mar 05, 2009 4:41 AM
# re: Express Yourself With Custom Expression Builders
Actually, you could in fact just use <%# with some code.

Expression builders have were mainly custom syntactic sugar that let you use arbitrary declarative syntax for databinding, rather than rely on language-specific code. They had two key pieces of value:

1) They also come with a design-time infrastructure (see the ExpressionEditor attribute) to let you build custom designers for an expression type.

2) They work in the non-compile page scenario, which lets you run an ASP.NET page under certain restrictions - most importantly, no custom code - without compiling the page into a class. A big reason why we built this mode in Whidbey was to support Sharepoint, which wanted to host many "virtual sites" inside a single appdomain, and didn't want each page to take the overhead of a class that couldn't be garbage collected away.

If you're building an app for yourself, you probably shouldn't be building an expression builder for it.
Requesting Gravatar... Shanku Niyogi Mar 05, 2009 4:45 AM
# re: Express Yourself With Custom Expression Builders
And, just to be complete, you can't actually support the non-compile scenario unless you also override EvaluateExpression. In the non-compile scenario, this method gets called to evaluate the expression without having to generate code.

What do you have to say?

(will show your gravatar)
Please add 7 and 5 and type the answer here: