A BuildProvider to simplify your ASP.NET MVC Action Links
Update: Please see this newer post for the latest and greatest MVC T4 template
One downside of using Html.ActionLink in your views is that it is late bound. e.g. say you write something like this:
<%= Html.ActionLink("Home", "Index", "Home")%>
The second parameter is the Action name, and the third is the Controller name. Note how they are both specified as plain strings. This means that if you rename either your Controller or Action, you will not catch the issue until you actually run your code and try to click on the link.
Now let’s take the case where you Action takes parameters, e.g.:
public ActionResult Test(int id, string name) {
return View();
}
Now your ActionLink calls looks something like this:
<%= Html.ActionLink("Test Link", "Test", "Home", new { id = 17, name = "David" }, null) %>
So in addition to the Controller and Action names changing, you are vulnerable to the parameter names changing, which again you won’t easily catch until runtime.
One approach to solving this is to rely on Lambda expressions to achieve strong typing (and hence compile time check). The MVC Futures project demonstrates this approach. It certainly has merits, but the syntax of Lambda expressions in not super natural to most.
Here, I’m exploring an alternative approach that uses an ASP.NET BuildProvider to generate friendlier strongly typed helpers. With those helpers, the two calls below become simply:
<%= Html.ActionLinkToHomeIndex("Home")%><%= Html.ActionLinkToHomeTest("Test Link", 17, "David")%>
Not only is this more concise, but it doesn’t hard code any of the problematic strings discussed above: the Controller and Action names, and the parameter names.
Steps to enable the ActionLink helpers in your MVC app
You can easily integrate these helpers in any ASP.NET MVC app by following three steps:
1. First, add a reference to MvcActionLinkHelper.dll in your app (build the project in the zip file attached to this post to get it)
2. Then, register the build provider in web.config. Add the following lines in the <compilation> section:
<buildProviders>
<add extension=".actions" type="MvcActionLinkHelper.MvcActionLinkBuildProvider" />
</buildProviders>
3. The third step is a little funky, but still easy. You need to create an App_Code folder in your app, and add a file with the .actions extension in it. It doesn’t matter what’s in the file, or what its full name is. e.g. add an empty file named App_Code/generate.actions. This file is used to trigger the BuildProvider.
How does it all work?
I included all the sources in the zip, so feel free to look and debug through it to see how it works. In a nutshell:
- When the first request is made at runtime, ASP.NET needs to build the App_Code assembly
- It finds our .actions file, which triggers the registered BuildProvider
- The BuildProvider goes through all the reference assemblies and looks for Controller classes
- It then generates a static class with extension methods for each action
- Since every aspx page is built with a reference to the App_Code assembly, all the views are able to use the generated helpers.
Where is this going?
At this point, this is just a quick proof of concept. There are certainly other areas of MVC where the same idea can be applied. e.g. currently it only covers Html.ActionLink, but could equally cover Url.Action(), or HTML form helpers (standard and AJAX).
Please send feedback whether you find this direction interesting as an alternative to the Lambda expression approach.
Comments
Anonymous
June 01, 2009
PingBack from http://asp-net-hosting.simplynetdev.com/a-buildprovider-to-simplify-your-aspnet-mvc-action-links/Anonymous
June 02, 2009
Thank you for submitting this cool story - Trackback from DotNetShoutoutAnonymous
June 02, 2009
Is there a way to tie this into the build with a post build event... sort of like the aspcompile task that was added with the 1.0 release?Anonymous
June 02, 2009
Eric, the use of BuildProvider makes it intrinsically something that gets generated at runtime rather than design time. If you precompile the app (e.g. using aspnet_compiler.exe), it will get compiled in there. But I'm not sure that it could be hooked into using a post build event.Anonymous
June 02, 2009
I see how the runtime compile is nice, I see that I can get intellisense support with VS but I loose the intelisense with my resharper plugin... :( It would be nice if I could specify my intellisense provider by file type (extension) and that would do what I need. It seems like the aspx web form editor must reference the dlls from the compiled (temp) directory, is that right?Anonymous
June 02, 2009
One downside of using Html.ActionLink in your views is that it is late bound.  e.g. say you writeAnonymous
June 02, 2009
Very nice, thank you. I modified it to include the Title attribute for the anchor tag and plan to use it on our current project. YES! to Url and Form helpers!Anonymous
June 02, 2009
Very nice! Thanks a lot for posting this.Anonymous
June 03, 2009
I prototyped a Url helper from your example... I do like this approach. I am thinking a syntax like. Url.Controller.Action would be interesting. Where I would use this in a form and I would expect all the parameters from my form post to come as form inputs . In this case I would make my "Action" a property rather than a method.Anonymous
June 03, 2009
Daily Tech Links - June 4, 2009 Web Development ASP.NET MVC Training Kit Visual Studio 2010: MultipleAnonymous
June 03, 2009
You've been kicked (a good thing) - Trackback from DotNetKicks.comAnonymous
June 04, 2009
Have you tested the performance gains of using this solution rather than the regular Url Helpers?Anonymous
June 04, 2009
Eric, I'm not sure I'm quite following your Url.Controller.Action idea. Can you include a small example of what the code you'll end up writing in the view would look like?Anonymous
June 04, 2009
Sruly, I have not done in formal perf testing, but those helpers should be basically equivalent to calling Html.ActionLink directly, which is probably the fastest method available. So it should be much faster than the helpers that are based on Lambda Expressions.Anonymous
June 04, 2009
Earlier this week, I wrote a post on using a BuildProvider to create ActionLink helpers .  ThatAnonymous
June 14, 2009
Our Html.ActionLink Helpers in ASP.NET MVC could use a little dash of named routes in Rails along with compile-time safety and refactoring support.Anonymous
June 14, 2009
Our Html.ActionLink Helpers in ASP.NET MVC could use a little dash of named routes in Rails along with compile-time safety and refactoring support.Anonymous
June 17, 2009
A couple weeks ago, I blogged about using a Build provider and CodeDom to generate strongly typed MVCAnonymous
June 21, 2009
Thanks this template is a great aid. I was wondering if this template could be modified to allow subfolders in the controllers Folder (all controllers in the same namespace) so that I can better organize them to avoid clutter.Anonymous
June 21, 2009
Bernard, did you mean to comment on my later post (A new and improved ASP.NET MVC T4 template)? This post doesn't involve a template.Anonymous
June 24, 2009
I've taken your idea a bit further by creating extension method for UrlHelper (e.g. UrlHelper.ActionToHomeIndex()). This works fine when I'm working in HTML, but I can't use the newly added helpers in class files. I've tried adding the same namespace to the top of the file with a using statement but still no joy. Am I missing something?Anonymous
June 24, 2009
Andrew, please see my later post using T4 template (http://blogs.msdn.com/davidebb/archive/2009/06/17/a-new-and-improved-asp-net-mvc-t4-template.aspx). It supports Url helpers, and works in the class files.Anonymous
June 28, 2009
Cheers for the new post link but I prefer the BuildProvider approach if I'm honest :-P. Is there no possible way to get this solution working in class files?Anonymous
June 28, 2009
Andrew: unfortunately, that is not really possible in a Web Application, because the class files are built by VS at design time, while the BuidlProvider is a runtime thing. So the class files simply can't reference that code. With a Web Site, this would work, though most MVC apps use Web Apps.Anonymous
July 19, 2009
Спасибо за частые обновления на блоге!!Anonymous
July 28, 2009
In the next version of the MVC framework will we get strongly typed action links that we can point to our view. The MVC Futures has it already. To me it's cleaner having an actionlink as something like: <%= Html.ActionLink<CalveNet.Controllers.PatwareController>(c => c.Index(), "Here")%> As opposed to: <% Html.ActionLinkToPatwareIndex() %>Anonymous
November 21, 2012
Why not use a T4 template instead of a build provider to generate the typed ActionLinks ? That whay when you build it's there, no need to fire up the app and wait for it to start to have the static generation.Anonymous
November 22, 2012
@Ricardo: see T4MVC (https://t4mvc.codeplex.com/) for a T4 based solution.Anonymous
November 22, 2012
Great stuff! Will definitely take a look. Cheers