ASP.NET Web Forms Extensibility: Expression Builders

I have talked extensively about expression builders in the past, and that is because I find them extremely useful, for building no code solutions.

In a nutshell, it is a design-time mechanism for executing a method which receives some parameters and returns something that will be bound to a control’s property. Anyone who has used resources – <%$ Expression:xxx %> -, bound a connection string or an application setting to a data source control – <%$ ConnectionStrings:xxx %> and <%$ AppSettings:xxx %> – on an ASP.NET page has used expression builders. These expression builders are included with ASP.NET, and do useful things, such as returning a connection string or an application setting from the configuration file, or returning a translated resource according to the current browser culture, without the need to code.

One example would be, for example, picking up some value from the current HttpContext and assigning it to some control’s property. Here’s a simple way to achieve it, using DataBinder.Eval to process possibly complex expressions:

   1: public class ContextExpressionBuilder : ExpressionBuilder
   2: {
   3:     #region Public static methods
   4:     public static Object GetValue(String expression, Type propertyType)
   5:     {
   6:         HttpContext context = HttpContext.Current;
   7:         Object expressionValue = DataBinder.Eval(context, expression.Trim().Replace('\'', '"'));
   8:  
   9:         expressionValue = Convert(expressionValue, propertyType);
  10:  
  11:         return (expressionValue);
  12:     }
  13:  
  14:     #endregion
  15:  
  16:     #region Public override methods
  17:     public override Object EvaluateExpression(Object target, BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
  18:     {
  19:         return (GetValue(entry.Expression, entry.PropertyInfo.PropertyType));
  20:     }
  21:     #endregion
  22:  
  23:     #region Protected static methods
  24:     protected static Object Convert(Object value, Type propertyType)
  25:     {
  26:         if (value != null)
  27:         {
  28:             if (propertyType.IsAssignableFrom(value.GetType()) == false)
  29:             {
  30:                 if (propertyType.IsEnum == true)
  31:                 {
  32:                     value = Enum.Parse(propertyType, value.ToString(), true);
  33:                 }
  34:                 else if (propertyType == typeof(String))
  35:                 {
  36:                     value = value.ToString();
  37:                 }
  38:                 else if ((typeof(IConvertible).IsAssignableFrom(propertyType) == true) && (typeof(IConvertible).IsAssignableFrom(value.GetType()) == true))
  39:                 {
  40:                     value = System.Convert.ChangeType(value, propertyType);
  41:                 }
  42:             }
  43:         }
  44:  
  45:         return (value);
  46:     }
  47:     #endregion
  48:  
  49:     #region Public override methods
  50:     public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, Object parsedData, ExpressionBuilderContext context)
  51:     {            
  52:         if (String.IsNullOrEmpty(entry.Expression) == true)
  53:         {
  54:             return (new CodePrimitiveExpression(String.Empty));
  55:         }
  56:         else
  57:         {
  58:             return (new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(this.GetType()), "GetValue"), new CodePrimitiveExpression(entry.Expression.Trim()), new CodeTypeOfExpression(entry.PropertyInfo.PropertyType)));
  59:         }
  60:     }
  61:     #endregion
  62:  
  63:     #region Public override properties
  64:     public override Boolean SupportsEvaluate
  65:     {
  66:         get
  67:         {
  68:             return (true);
  69:         }
  70:     }
  71:     #endregion
  72: }

As you can see, there are two methods that may be called in order to retrieve a value:

In this example, I have implemented both methods, so that it can be used in various scenarios. On the GetCodeExpression method I simply return a Code DOM expression that calls the static method GetValue on my expression builder. I have a method that tries to convert the returned value into the target property on the declaring control, in case they are of different types.

Expression builders need to be registered on the Web.config file, on the expressionBuilders section:

   1: <configuration>
   2:     <system.web>
   3:         <compilation>
   4:             <expressionBuilders>
   5:                 <add expressionPrefix="Context" type="MyNamespace.ContextExpressionBuilder, MyAssembly"/>
   6:             </expressionBuilders>
   7:         </compilation>

Finally, here’s how to use this sample expression builder to feed a Label text on a markup file:

   1: <span>Value of cookie MyId: <asp:Label runat="server" Text="<%$ Context:Request.Cookies['MyId'].Value %>" /></span>

This is identical to having the following code:

   1: myLiteral.Text = HttpContext.Current.Request.Cookies["MyId"].Value;

The syntax is <%$ MyRegisteredPrefix: SomeString %>, where MyRegisteredPrefix is what you want it to be, and what is registered on Web.config, and SomeString is also a randomly-formed string. Note that the expression builders forbid us to using in this string, so, what I did was allow to be used instead, and replace them at runtime.

What remains to be said is regarding editors for allowing integrated expression builder design inside Visual Studio, but since that is strictly not required, I leave it to some other time.

As usual, hope you find this of use. Look for the other expression builders that I wrote, all code is available on the blog.

                             

1 Comment

Comments have been disabled for this content.