I have moved my active blog over to tumblr. I've maintained this blog for reference but will be posting to http://www.robustsoftware.co.uk instead. I've pointed my Feedburner feed to tumblr so if you're subscribed already you should already have switched with me.

Strongly typed links for ASP.NET MVC through the power of LINQ expressions

Wouldn't it be nice if you could have strongly typed links in your ASP.NET MVC views rather than a having to use floaty strings? Something like this for example:


    1 <%= Html.LinkTo<ProjectsController>("Settings", c => c.Index()) %>



Or something similar with an id argument?


    1 <%= Html.LinkTo<ProjectsController>(ViewData.Model.Name, c => c.Details(ViewData.Model.Id)) %>



Well utilising the awesomeness of LINQ expressions, generics and extension methods (that's right, 3 kinds of awesome, at the same time) you can:


    1 public static string LinkTo<TController>(this HtmlHelper helper, string linkText,

    2     Expression<Func<TController, ActionResult>> action) where TController : IController

    3 {

    4     object routeValues;

    5 

    6     var methodExpression = action.Body as MethodCallExpression;

    7     var actionName = methodExpression.Method.Name;

    8     var controllerName = methodExpression.Object.Type.Name.Replace("Controller", string.Empty);

    9 

   10     // is there an id parameter on the action method being called?

   11     if (methodExpression.Method.GetParameters().Where(p => p.Name == "id").SingleOrDefault() != null)

   12     {

   13         // yes - retrieve the value of the id through magicks

   14         var idPropertyExpression = methodExpression.Arguments[0];

   15         var id = Expression.Lambda(idPropertyExpression).Compile().DynamicInvoke();

   16 

   17         routeValues = new { controller = controllerName, id };

   18     }

   19     else

   20     {

   21         routeValues = new { controller = controllerName };

   22     }

   23 

   24     return helper.ActionLink(linkText, actionName, routeValues);

   25 }



So what's going on here?

On lines 1 and 2 we are defining a generic signature for an extension of the HtmlHelper class which can only be used by implementations of IController (the interface which all ASP.NET MVC controllers must implement). This extension method takes two arguments, the text to display for the link and an Expression for a call to one of the actions on the controller.

So what does this buy us?

Well by casting the body of the Expression as a MethodCallExpression (line 6) we can gain access to the name of the method being called (line 7) and the name of the controller (line 8).

We can then look further at the method being called to see if it has a parameter with the name "id" (line 11). If there is such a parameter we begin to descend into the witchcraft of expression trees.

Firstly we grab the node of the expression tree which is used for the "id" parameter which we are assuming will be the first parameter (line 14). We can then use this node's Expression to create a LambdaExpression (line 15) which then means we can compile and invoke the node to retrieve the value the Expression represented. The way that this works allows you to use any statement that will produce an integer value within the expression. Either a property on an object as in the example or a regular integer will work.

We use the values we have retrieved from the Expression to create an anonymous type to be passed to the regular ActionLink extension. If there is no "id" parameter we will just create an anonymous type with only a value for the controller.

These are then passed to the regular ActionLink method but we have been hidden from all the floaty string nastiness.

Please note, this isn't a complete solution, more a proof of concept.

Clearing the cache of a LINQ to SQL DataContext

I was having some problems with my test code in that the DataContext was returning the objects from the cache (even though it was querying the database) and this was skewing my integration tests. Through the use of Reflector I found that there actually is a method for clearing the cache, handily called ClearCache(). However, the method was internal and so could not be called directly from code.

Internal methods are, however, available through reflection (I'm a bit of a noob when it comes to reflection so there might be constraints on when they are and when they aren't but this works for me). In order to make it easy to clear the cache (for example, after saving but before retrieving the object from the database for checking it saved correctly) I have created an extension method to do the heavy lifting for me:

public static void ClearCache(this ProjectSupportContext context)

{

    const BindingFlags FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

 

    var method = context.GetType().GetMethod("ClearCache", FLAGS);

 

    method.Invoke(context, null);

}

Hope this helps someone else out with LINQ to SQL caching woes.

Making LINQ to SQL associations perform inner joins

Today I had a problem whereby I had created an association but when the data for my object was retrieved it was joining the tables through a left outer join.

As these joins were non-nullable I wanted to make them perform an inner join to take advantage of the greater performance. In order to do this you need to set the ForeignKey attribute to "true" and CanBeNull to "false". The omission or reversal of either of these attributes will mean LINQ to SQL will assume that the parent object may not exist and will instead perform a left outer join

This will mean you end up with a XML mapping such as:

<Association Name=”SomeAssociation” Member=”Project” ThisKey=”ProjectId”
OtherKey=”Id” ForeignKey=”true” CanBeNull=”false” />

Of course you also need to create a DataLoadOption so that the parent object gets retrieved along with the child (eagerly):

var options = new DataLoadOptions();

options.LoadWith<Job>(j => j.Project);

context.LoadOptions = options;