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) %>


<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…


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>.

Leave a Reply

Your email address will not be published. Required fields are marked *