{"id":106,"date":"2010-05-02T23:39:20","date_gmt":"2010-05-02T23:39:20","guid":{"rendered":"http:\/\/blog.joefield.co.uk\/?p=106"},"modified":"2010-05-04T09:05:04","modified_gmt":"2010-05-04T09:05:04","slug":"digging-around-fasttracktmodel-tvalue-in-asp-net-mvc-2","status":"publish","type":"post","link":"https:\/\/blog.joefield.co.uk\/?p=106","title":{"rendered":"Digging around: FastTrack in ASP.NET MVC 2"},"content":{"rendered":"<p>This is a second companion post to Scott Guthrie&#8217;s <a href=\"http:\/\/weblogs.asp.net\/scottgu\/archive\/2010\/01\/10\/asp-net-mvc-2-strongly-typed-html-helpers.aspx\" target=\"_blank\">ASP.NET MVC 2: Strongly Typed Html Helpers<\/a>.<\/p>\n<p>In my <a href=\"http:\/\/blog.joefield.co.uk\/?p=33\" target=\"_blank\">previous post<\/a> I drilled down into the code executed when ASP.NET MVC 2 turned:<\/p>\n<pre class=\"brush: xml;\">&lt;%= Html.TextBoxFor(model =&gt; model.ProductName) %&gt;<\/pre>\n<p>into:<\/p>\n<pre class=\"brush: xml;\">&lt;input id=&quot;ProductName&quot; name=&quot;ProductName&quot; type=&quot;text&quot; value=&quot;Aniseed Syrup&quot; \/&gt;<\/pre>\n<p>We found that in order to do it ASP.NET MVC had performed two distinct operations:<\/p>\n<ol>\n<li>parsed the expression tree produced by the compiler for <strong>model =&gt; model.ProductName<\/strong> to extract the name of the property (&quot;ProductName&quot;) to use in the id and name attributes of the input element<\/li>\n<li>compiled the expression and invoked the resulting delegate to produce the &quot;Aniseed Syrup&quot; value<\/li>\n<\/ol>\n<p>Having examined the first case I started looking at how the expression was compiled into an executable delegate.<\/p>\n<p>At this point we ran into this line of code:<\/p>\n<pre class=\"brush: csharp;\">return CachedExpressionCompiler.Process(expression)(container);<\/pre>\n<h4>The problem<\/h4>\n<p>MVC is faced with a dilemma. It wants to use expression trees rather than delegates.<\/p>\n<p>It likes to use the metadata obtained from the expression to, amongst other things, extract the property name.<\/p>\n<p><strong>But compiling expression trees is expensive.<\/strong><\/p>\n<p><strong><em>To get round this problem it maintains an in-process cache of compiled expressions.<\/em><\/strong><\/p>\n<h4>Disclaimer &#8211; this is a simple case<\/h4>\n<p>We are only going to look at the simple property access case:<\/p>\n<pre class=\"brush: csharp;\">model =&gt; model.ProductName<\/pre>\n<p>If we didn&#8217;t we would vanish into CachedExpressionCompiler for a long time. Many different types of expressions are compiled and cached, using different strategies.<\/p>\n<p>By only looking at the property access case we will be able to visit the main classes, in particular FastTrack&lt;TModel, TValue&gt;.&#160; We can look at the other caching strategies another time.<\/p>\n<h4>CachedExpressionCompiler.Process method<\/h4>\n<p>Here&#8217;s the CachedExpressionCompiler.Process() method in full &#8211; the comment describes it way better than I just did:<\/p>\n<pre class=\"brush: csharp;\">\/\/ This is the entry point to the cached expression tree compiler. The processor will perform a series of checks \r\n\/\/ and optimizations in order to return a fully-compiled func as quickly as possible to the caller. If the \r\n\/\/ input expression is particularly obscure, the system will fall back to a slow but correct compilation step. \r\npublic static Func&lt;TModel, TValue&gt; Process&lt;TModel, TValue&gt;(Expression&lt;Func&lt;TModel, TValue&gt;&gt; lambdaExpression) { \r\n    return Processor&lt;TModel, TValue&gt;.GetFunc(lambdaExpression); \r\n} <\/pre>\n<p>and here&#8217;s the beginning of Processor.GetFunc():<\/p>\n<pre class=\"brush: csharp;\">public static Func&lt;TModel, TValue&gt; GetFunc(Expression&lt;Func&lt;TModel, TValue&gt;&gt; lambdaExpression) { \r\n    \/\/ look for common patterns that don't need to be fingerprinted \r\n    Func&lt;TModel, TValue&gt; func = GetFuncFastTrack(lambdaExpression); \r\n    if (func != null) { \r\n        return func; \r\n    } \r\n... \r\n... \r\n... \r\n}<\/pre>\n<p>We&#8217;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.<\/p>\n<p>Here&#8217;s the whole of Processor.GetFuncFastTrack():<\/p>\n<pre class=\"brush: csharp;\">private static Func&lt;TModel, TValue&gt; GetFuncFastTrack(Expression&lt;Func&lt;TModel, TValue&gt;&gt; lambdaExpression) { \r\n    ParameterExpression modelParameter = lambdaExpression.Parameters[0]; \r\n    Expression body = lambdaExpression.Body; \r\n\r\n    return FastTrack&lt;TModel, TValue&gt;.GetFunc(modelParameter, body); \r\n}<\/pre>\n<p>The original expression has been split into its single parameter (our model) and its body and pased to the FastTrack class.<\/p>\n<h4>FastTrack&lt;TModel, TValue&gt;.GetFunc method<\/h4>\n<p>And finally here&#8217;s the relevant portion of the FastTrack&lt;TModel, TValue&gt;.GetFunc() method.<\/p>\n<pre class=\"brush: csharp;\">public static Func GetFunc(ParameterExpression modelParameter, Expression body) { \r\n    ... \r\n    ... \r\n    ... \r\n    { \r\n        MemberExpression memberExpression = body as MemberExpression; \r\n        if (memberExpression != null) { \r\n            ... \r\n            ... \r\n            ... \r\n            else if (memberExpression.Expression == modelParameter) { \r\n                \/\/ model =&gt; model.Member \r\n                return GetModelMemberLookupFunc(memberExpression.Member, false \/* isStatic *\/); \r\n            } \r\n            ... \r\n            ... \r\n            ... \r\n        } \r\n    } \r\n    ... \r\n    ... \r\n    ... \r\n}<\/pre>\n<p>As you can see the expression body has been successfully cast to a MemberExpression.&#160; <\/p>\n<p>A MemberExpression has two public properties:<\/p>\n<ul>\n<li>Expression &#8211; which represents the containing object (our &quot;model&quot;)<\/li>\n<li>Member &#8211; which is the MemberInfo instance representing the access (our &quot;ProductName&quot;)<\/li>\n<\/ul>\n<p>MVC compares the Expression property to the ParameterExpression.&#160; They are equal so it calls:<\/p>\n<pre class=\"brush: csharp;\">GetModelMemberLookupFunc(memberExpression.Member, false \/* isStatic *\/);<\/pre>\n<p>which is just a wrapper for this:<\/p>\n<pre class=\"brush: csharp;\">return _modelMemberLookupCache.GetFunc(member, isStatic);<\/pre>\n<p>I sense that we&#8217;re getting close to a cache&#8230;<\/p>\n<h4>ModelMemberLookupCache<\/h4>\n<p>This _modelMemberLookupCache is a static member of type ModelMemberLookupCache.&#160; ModelMemberLookupCache is derived from an abstract base class called ReaderWriteCache&lt;TKey, TValue&gt;.&#160; It provides a creation function for the case when the cache lookup fails i.e. the first time.&#160; Here&#8217;s the creation function:<\/p>\n<pre class=\"brush: csharp;\">private static Func&lt;TModel, TValue&gt; CreateFunc(MemberInfo member, bool isStatic) { \r\n    ParameterExpression modelParam = Expression.Parameter(typeof(TModel), &quot;model&quot;); \r\n    MemberExpression memberExpr = Expression.MakeMemberAccess((!isStatic) ? modelParam : null, member); \r\n    Expression&lt;Func&lt;TModel, TValue&gt;&gt; lambda = Expression.Lambda&lt;Func&lt;TModel, TValue&gt;&gt;(memberExpr, modelParam); \r\n    return lambda.Compile(); \r\n}<\/pre>\n<p>In effect it recreates our original expression but &quot;standardises&quot; it by replacing the name of the parameter with &quot;model&quot;.&#160; This is then compiled into a delegate and stored in the cache with the MemberInfo instance as the key.<\/p>\n<p>So the next time ASP.NET MVC encounters:<\/p>\n<pre class=\"brush: csharp;\">model =&gt; model.ProductName<\/pre>\n<p>it will be able to return a pre-compiled delegate from the cache &#8211; a significant boost to performance.<\/p>\n<p>Next I&#8217;ll look at the other cases FastTrack caters for, and dig around a bit more in ReaderWriterCache&lt;TKey, TValue&gt;.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is a second companion post to Scott Guthrie&#8217;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: &lt;%= Html.TextBoxFor(model =&gt; model.ProductName) %&gt; into: &lt;input id=&quot;ProductName&quot; name=&quot;ProductName&quot; type=&quot;text&quot; value=&quot;Aniseed Syrup&quot; \/&gt; We found that in order to do it ASP.NET MVC [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[6],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.joefield.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/106"}],"collection":[{"href":"https:\/\/blog.joefield.co.uk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.joefield.co.uk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.joefield.co.uk\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.joefield.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=106"}],"version-history":[{"count":8,"href":"https:\/\/blog.joefield.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/106\/revisions"}],"predecessor-version":[{"id":115,"href":"https:\/\/blog.joefield.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/106\/revisions\/115"}],"wp:attachment":[{"href":"https:\/\/blog.joefield.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=106"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.joefield.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=106"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.joefield.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=106"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}