Managing Links and URLs with T4MVC

To help tame large applications and organize site functionality, ASP.NET MVC 2 introduced the concept of areas. Areas allow you to segregate controllers, models, and views into different physical locations, with the area-specific pieces in a single area folder. This article shows how T4MVC templates help you generate URLs and links between areas.

This article is based on ASP.NET MVC 4 in Action, published in May 2012. It is being reproduced here by permission from Manning Publications. Manning early access books and ebooks are sold exclusively through Manning. Visit the book’s page for more information.

Authors: Jeffrey Palermo, Jimmy Bogard, Eric Hexter, Matthew Hinze, and Jeremy Skinner

ASP.NET MVC 4 in Action Out of the box, ASP.NET MVC contains many opportunities to get tripped up with magic strings, especially with URL generation. Magic strings are string constants that are used to represent other constructs, but with an added disconnect that can lead to subtle errors that only show up at runtime. For example, many ASP.NET MVC methods accept string parameters that refer to controller classes and action methods. Renaming the controller or action does not update these strings, causing the application to break at runtime. To provide some intelligence around referencing controllers, views, and actions, the T4MVC project helps by generating a hierarchical code model representation for use inside controllers and views. The T4MVC project uses Microsoft’s T4 templating engine to provide a simplified way to reference controllers, actions, and views.

In the next listing, our Edit action contains a BeginForm method call that references the Save action on the Profile controller, using magic strings to build the URL for the form element.

Listing 1 A brittle Edit view with magic strings
@using (Html.BeginForm("Save", "Profile")) {
@Html.EditorForModel()
<p>
<input type="submit" value="Save" name="SaveButton" />
</p>
}

The magic strings in listing 1 lie in the Html.BeginForm method. The strings “Save” and “Profile” are route data that refer to a ProfileController class and Save method. If we were to change the name of our controller and action via built-in refactoring tools, our Edit view would then break. Ideally, all the places where we reference controllers, actions, views, and route values by magic strings could be replaced by something more resilient to the inevitable changes in most projects. Hard-coded route data values reference “area”. If we were to accidentally mistype or misspell the area route entry or value, our application would break at runtime.

To eliminate these potential problems, we have two options. We can use constants and strongly typed, expression-based URL generation, or we can use a form of code generation that allows us to easily reference views, controllers, and actions. The T4MVC project, which is part of MvcContrib, uses T4 (Text Template Transformation Toolkit) templates to generate extension methods, view name constants, and action link helpers to eliminate the pesky magic strings that would otherwise litter our application. The T4MVC templates use the T4 templating technology introduced with Visual Studio 2008.

To use T4MVC, you first need to download the latest T4MVC release from http://mvccontrib.codeplex.com/wikipage?title=T4MVC and place the following two files in the root of your application:
* T4MVC.tt
* T4MVC.settings.t4

Alternatively, you can use the NuGet package management utility to install T4MVC. In figure 1, you can see these two files added to the root of our MVC application.

 

Our application, including the two T4MVC template files

Figure 1 – Our application, including the two T4MVC template files

When the T4MVC templates are added to the project, or when the project is built or run, the templates are regenerated. In some environments, a security dialog box may pop up, as shown in figure 2.

The T4 template security dialog box

Figure 2 – The T4 template security dialog box

You can check the Do Not Show This Message Again check box if you don’t want this dialog box showing up again, and click the OK button to run the template generation.

The T4MVC template modifies existing controllers, making them partial classes, and generates a set of helper files. These helper files, shown in figure 3, include a set of code-generated controller partial classes and extension methods.

Helper files generated from the T4MVC templates

Figure 3 – Helper files generated from the T4MVC templates

With partial classes, the T4MVC templates generate a set of helper methods and properties that allow us to easily refer to controllers, actions, and views from anywhere in our application. For example, the original LogOff action in the AccountController was rife with magic strings, as shown here.

Listing 2 The original LogOff action
public virtual ActionResult LogOff()
{
FormsService.SignOut();
return RedirectToAction("Index", "Home");
}

Instead of referring to the Index action on the Home controller by strings, we can instead navigate the hierarchy created in the generated MVC class:

Listing 3 Using the generated MVC class to refer to controllers and actions
public virtual ActionResult LogOff()
{
FormsService.SignOut();
return RedirectToAction(MVC.Home.Index());
}

Internally, the new RedirectToAction method lives on the generated partial controller class. The Index method in listing 3 records the controller and action name, allowing the generated RedirectToAction method to build the correct ActionResult. All of this is behind the scenes, and our existing controllers can start using the new generated overloads to generate ActionResult objects.

In our views, we’ll use some generated HtmlHelper extension methods for generating action links and URLs. Here’s our modified logon partial:

Listing 4 Using the generated HtmlHelper extension methods
@if (Request.IsAuthenticated) {
<text>Welcome <b>@Context.User.Identity.Name</b>!
[ @Html.ActionLink("Log Off", MVC.Account.LogOff())
|
@Html.ActionLink("Profile",
MVC.Admin.Profile.Show(Context.User.Identity.Name))
]
</text>
} else {
@:[ @Html.ActionLink("Log On", MVC.Account.LogOn()) ]
}

Instead of supplying the area route information manually, we navigate a logical controller hierarchy structure. The ProfileController resides in the Admin area, and the generated helper class is located in an Admin property. The class hierarchy generated by T4MVC matches the area and controller layout of our project. If we were to rename an action method, we’d simply need to regenerate the templates and our code would be updated accordingly. The methods referring to actions also include overloads that accept the original action parameters, allowing us to easily supply route information for action parameters. The Show action accepts a username parameter, which we simply pass in directly.

Code generation can be quite powerful, but it does come with some caveats. You need to remember to run the templates when your application changes, and running the code generation takes longer as your project grows. Although code generation helps prevent runtime errors, it moves them to compile time instead of eliminating them entirely. Code generation is still not resilient to refactoring, but T4MVC is a powerful tool that can eliminate much of the magic string proliferation in ASP.NET MVC applications.

Summary

Large MVC applications can become quite unwieldy to manage. To tame the natural organization that sites with many different sections and areas have, you can use the areas feature introduced in ASP.NET MVC 2.0. These MVC areas allow you to segregate content into logical and physical folders, each with their own shared content hidden from other areas.

For global content, you can still take advantage of global shared content. With the added flexibility of areas comes some added work when generating URLs from routes to ensure that the URLs work across areas. To help with this URL generation, you can use the T4MVC project. T4MVC uses the T4 templating technology to generate code-beside partial classes for your controllers, providing easy access to a hierarchical structure describing the controllers, actions, and views in your site.

Related Videos: