Category Archives: ASP.NET MVC

Digging around: Analysing ASP.NET MVC2 FastTrack expressions

This is a third companion post to Scott Guthrie’s ASP.NET MVC 2: Strongly Typed Html Helpers.

Today instead of reading code, I’d thought I’d write some. I threw together a class which does the analysis performed by the FastTrack class in ASP.NET MVC2. To recap – the FastTrack class:

  • decides if an expression can be “fast tracked”
  • if it can – builds, compiles and caches it
  • returns the cached expression in future calls

This code demonstrates the bit that does the deciding, and gives us something to play around with.

using System;
using System.Linq.Expressions;

namespace MvcHarness
{
  public class FastTrackAnalyser<TModel>
  {
    public void Analyse<TValue>(Expression<Func<TModel, TValue>> lambda)
    {
      ParameterExpression modelParameter = lambda.Parameters[0];
      Expression body = lambda.Body;

      if (modelParameter == body)
      {
        Console.WriteLine("{0} is a function returning its own argument", lambda);
        return;
      }

      ConstantExpression constantExpression = body as ConstantExpression;
      if (constantExpression != null)
      {
        Console.WriteLine("{0} is a function returning a constant value", lambda);
        return;
      }

      MemberExpression memberExpression = body as MemberExpression;
      if (memberExpression != null)
      {
        if (memberExpression.Expression == null)
        {
          Console.WriteLine("{0} is a function returning a static member of either the model or another type", lambda);
          return;
        }

        if (memberExpression.Expression == modelParameter)
        {
          Console.WriteLine("{0} is a function returning a member of the model", lambda);
          return;
        }

        ConstantExpression constantExpression2 = memberExpression.Expression as ConstantExpression;
        if (constantExpression2 != null)
        {
          Console.WriteLine("{0} is a function representing a captured local variable", lambda);
          return;
        }
      }
      Console.WriteLine("{0} cannot be fast tracked", lambda);
    }
  }
}


I called it like this (with output inline in the comments):

using System;

namespace MvcHarness
{
  class Program
  {
    static void Main(string[] args)
    {
      FastTrackAnalyser<string> analyser = new FastTrackAnalyser<string>();

      analyser.Analyse(x => x);
      // outputs "x => x is a function returning its own argument"

      analyser.Analyse(x => 999);
      // outputs "x => 999 is a function returning a constant value"

      analyser.Analyse(x => String.Empty);
      // outputs "x => String.Empty is a function returning a static member of either the model or another type"

      analyser.Analyse(x => x.Length);
      // outputs "x => x.Length is a function returning a member of the model"

      string name = "Joe Field";
      analyser.Analyse(x => name);
      // outputs "x => value(MvcHarness.Program+<>c__DisplayClass0).name is a function representing a captured local variable"

      analyser.Analyse(x => name.Length);
      // outputs "x => value(MvcHarness.Program+<>c__DisplayClass0).name.Length cannot be fast tracked"
    }
  }
}

The really interesting calls are the last two. They are analysing expressions which use captured local variables. The first one succeeds, but the second fails because it attempts to access a member of the variable.

Digging around: FastTrack in ASP.NET MVC 2

This is a second companion post to Scott Guthrie’s ASP.NET MVC 2: Strongly Typed Html Helpers.

In my previous post I drilled down into the code executed when ASP.NET MVC 2 turned:

<%= Html.TextBoxFor(model => model.ProductName) %>

into:

<input id="ProductName" name="ProductName" type="text" value="Aniseed Syrup" />

We found that in order to do it ASP.NET MVC had performed two distinct operations:

  1. parsed the expression tree produced by the compiler for model => model.ProductName to extract the name of the property ("ProductName") to use in the id and name attributes of the input element
  2. compiled the expression and invoked the resulting delegate to produce the "Aniseed Syrup" value

Having examined the first case I started looking at how the expression was compiled into an executable delegate.

At this point we ran into this line of code:

return CachedExpressionCompiler.Process(expression)(container);

The problem

MVC is faced with a dilemma. It wants to use expression trees rather than delegates.

It likes to use the metadata obtained from the expression to, amongst other things, extract the property name.

But compiling expression trees is expensive.

To get round this problem it maintains an in-process cache of compiled expressions.

Disclaimer – this is a simple case

We are only going to look at the simple property access case:

model => model.ProductName

If we didn’t we would vanish into CachedExpressionCompiler for a long time. Many different types of expressions are compiled and cached, using different strategies.

By only looking at the property access case we will be able to visit the main classes, in particular FastTrack<TModel, TValue>.  We can look at the other caching strategies another time.

CachedExpressionCompiler.Process method

Here’s the CachedExpressionCompiler.Process() method in full – the comment describes it way better than I just did:

// This is the entry point to the cached expression tree compiler. The processor will perform a series of checks 
// and optimizations in order to return a fully-compiled func as quickly as possible to the caller. If the 
// input expression is particularly obscure, the system will fall back to a slow but correct compilation step. 
public static Func<TModel, TValue> Process<TModel, TValue>(Expression<Func<TModel, TValue>> lambdaExpression) { 
    return Processor<TModel, TValue>.GetFunc(lambdaExpression); 
} 

and here’s the beginning of Processor.GetFunc():

public static Func<TModel, TValue> GetFunc(Expression<Func<TModel, TValue>> lambdaExpression) { 
    // look for common patterns that don't need to be fingerprinted 
    Func<TModel, TValue> func = GetFuncFastTrack(lambdaExpression); 
    if (func != null) { 
        return func; 
    } 
... 
... 
... 
}

We’ll talk about fingerprinting another day. Suffice it to say that the call to GetFuncFastTrack() succeeds for our expression and we need go no further.

Here’s the whole of Processor.GetFuncFastTrack():

private static Func<TModel, TValue> GetFuncFastTrack(Expression<Func<TModel, TValue>> lambdaExpression) { 
    ParameterExpression modelParameter = lambdaExpression.Parameters[0]; 
    Expression body = lambdaExpression.Body; 

    return FastTrack<TModel, TValue>.GetFunc(modelParameter, body); 
}

The original expression has been split into its single parameter (our model) and its body and pased to the FastTrack class.

FastTrack<TModel, TValue>.GetFunc method

And finally here’s the relevant portion of the FastTrack<TModel, TValue>.GetFunc() method.

public static Func GetFunc(ParameterExpression modelParameter, Expression body) { 
    ... 
    ... 
    ... 
    { 
        MemberExpression memberExpression = body as MemberExpression; 
        if (memberExpression != null) { 
            ... 
            ... 
            ... 
            else if (memberExpression.Expression == modelParameter) { 
                // model => model.Member 
                return GetModelMemberLookupFunc(memberExpression.Member, false /* isStatic */); 
            } 
            ... 
            ... 
            ... 
        } 
    } 
    ... 
    ... 
    ... 
}

As you can see the expression body has been successfully cast to a MemberExpression. 

A MemberExpression has two public properties:

  • Expression – which represents the containing object (our "model")
  • Member – which is the MemberInfo instance representing the access (our "ProductName")

MVC compares the Expression property to the ParameterExpression.  They are equal so it calls:

GetModelMemberLookupFunc(memberExpression.Member, false /* isStatic */);

which is just a wrapper for this:

return _modelMemberLookupCache.GetFunc(member, isStatic);

I sense that we’re getting close to a cache…

ModelMemberLookupCache

This _modelMemberLookupCache is a static member of type ModelMemberLookupCache.  ModelMemberLookupCache is derived from an abstract base class called ReaderWriteCache<TKey, TValue>.  It provides a creation function for the case when the cache lookup fails i.e. the first time.  Here’s the creation function:

private static Func<TModel, TValue> CreateFunc(MemberInfo member, bool isStatic) { 
    ParameterExpression modelParam = Expression.Parameter(typeof(TModel), "model"); 
    MemberExpression memberExpr = Expression.MakeMemberAccess((!isStatic) ? modelParam : null, member); 
    Expression<Func<TModel, TValue>> lambda = Expression.Lambda<Func<TModel, TValue>>(memberExpr, modelParam); 
    return lambda.Compile(); 
}

In effect it recreates our original expression but "standardises" it by replacing the name of the parameter with "model".  This is then compiled into a delegate and stored in the cache with the MemberInfo instance as the key.

So the next time ASP.NET MVC encounters:

model => model.ProductName

it will be able to return a pre-compiled delegate from the cache – a significant boost to performance.

Next I’ll look at the other cases FastTrack caters for, and dig around a bit more in ReaderWriterCache<TKey, TValue>.

Digging around: TextBoxFor in ASP.NET MVC 2

This is a companion post to Scott Guthrie’s ASP.NET MVC 2: Strongly Typed Html Helpers.

I was interested in what made this code:

<%= Html.TextBoxFor(model => model.ProductName) %>

in an aspx view template produce this html:

<input id="ProductName" name="ProductName" type="text" value="Aniseed Syrup" />

In order to do it ASP.NET MVC has performed two distinct operations:

  1. parsed the expression tree produced by the compiler for model => model.ProductName to extract the name of the property ("ProductName") to use in the id and name attributes of the input element
  2. compiled the expression and invoked the resulting delegate to produce the "Aniseed Syrup" value

TextBoxFor extension method

The call to Html.TextBoxFor ends up in the following extension method in the InputExtensions class:

public static MvcHtmlString TextBoxFor<TModel, TProperty>( 
    this HtmlHelper<TModel> htmlHelper, 
    Expression<Func<TModel, TProperty>> expression, 
    IDictionary<string, object> htmlAttributes) 
{ 
    return TextBoxHelper( 
        htmlHelper, 
        ModelMetadata.FromLambdaExpression(
            expression, 
            htmlHelper.ViewData).Model, 
        ExpressionHelper.GetExpressionText(expression), 
        htmlAttributes); 
}

I’ve highlighted the second and third parameters. The call to TextBoxHelper() does the final rendering by:

  • calling Convert.ToString() on the second parameter to populate the value attribute of the input element
  • using the third parameter to populate the name and id attributes of the input element

But let’s backtrack a bit first and look at the TextBoxFor signature itself.

Quick detour to Expression<Func<TModel, TProperty>>

Here’s the signature of the TextBoxFor method in the previous section:

public static MvcHtmlString TextBoxFor<TModel, TProperty>( 
    this HtmlHelper<TModel> htmlHelper, 
    Expression<Func<TModel, TProperty>> expression, 
    IDictionary<string, object> htmlAttributes) 
{ 
... 
... 
} 

By declaring a parameter of type Expression<Func<TModel, TProperty>> the TextBoxFor method has asked the compiler to generate an expression tree rather than a delegate.

Expression<TDelegate> is derived from LambdaExpression, which represents an expression tree which can be compiled into a delegate by calling its Compile() method.

Interestingly, the implementation of Compile() in Expression<TDelegate> calls Compile() on its base and casts the resulting delegate to TDelegate, so its a pretty thin wrapper for LambdaExpression.

All these uses of the term "lambda expression" can be mighty confusing. We call

model => model.ProductName

a lambda expression when we see it in code. The compiler however will treat it as an anonymous function or an expression tree, depending on the context

If it’s being assigned to Func<TModel, TProperty> it is treated as an anonymous function.

But if it’s being assigned to Expression<Func<TModel, TProperty>> then it is treated as an expression tree.

Anyway, back to the TextBoxFor method.

ExpressionHelper.GetExpressionText

public static MvcHtmlString TextBoxFor<TModel, TProperty>( 
    this HtmlHelper<TModel> htmlHelper, 
    Expression<Func<TModel, TProperty>> expression, 
    IDictionary<string, object> htmlAttributes) 
{ 
    return TextBoxHelper( 
        htmlHelper, 
        ModelMetadata.FromLambdaExpression(
            expression, 
            htmlHelper.ViewData).Model, 
        ExpressionHelper.GetExpressionText(expression), 
        htmlAttributes); 
} 

We want to get the text with which to populate the id and name attributes of our text box.

Because MVC has an expression tree rather than a delegate it can can pick it apart to find out, in this case, the name of the property being accessed.

Here’s my stripped down version of the implementation of GetExpressionText(), which covers the case of our simple model.ProductName property access:

public static string GetExpressionText(LambdaExpression expression) 
{ 
    MemberExpression memberExpression = (MemberExpression)expression.Body; 
    return memberExpression.Member.Name; 
}

The body of the expression is cast to a MemberExpression so that its Member.Name property can be accessed.

This returns "ProductName".

That was easy. Now on to the evaluation of the expression itself.

ModelMetadata.FromLambdaExpression

public static MvcHtmlString TextBoxFor<TModel, TProperty>( 
    this HtmlHelper<TModel> htmlHelper, 
    Expression<Func<TModel, TProperty>> expression, 
    IDictionary<string, object> htmlAttributes) 
{ 
    return TextBoxHelper( 
        htmlHelper, 
        ModelMetadata.FromLambdaExpression( 
            expression, 
            htmlHelper.ViewData).Model, 
        ExpressionHelper.GetExpressionText(expression), 
        htmlAttributes); 
} 

OK so we’ve got hold of of the string