Turn on Compile-time View Checking for ASP.NET MVC Projects in TFS Build 2010

When you create a View for an ASP.NET MVC web application, you add an .ASPX or .ASCX file that uses the ASP.NET engine to render a combination of mark-up and code. Unfortunately, those Views don’t get compiled until IIS renders them for your web site which means that something as simple as a typo in a namespace could break your site. You could manually visit each and every view to verify that it compiles successfully or you could create automated UI tests that do it for you. Fortunately, there’s an easier way to verify your views at compile-time.

To let you detect these errors at compile time, ASP.NET MVC projects now include an MvcBuildViews property, which is disabled by default. To enable this property, open the project file and set the MvcBuildViews property to true, as shown in this example from the ASP.NET MVC Release Notes:

 <PropertyGroup>
  <MvcBuildViews>true</MvcBuildViews>
</PropertyGroup>

Note that enabling this feature will add some overhead to your build process.

Unfortunately, this feature won’t work out of the box with TFS Build 2010 because of the way the post-build processing is done. If you look at the .csproj file for your ASP.NET MVC project, you’ll see an overridden AfterBuild target that invokes the AspNetCompiler task on your web application project’s sources. The problem here is two-fold:

  1. The AfterBuild target shouldn’t be overridden directly since that’s reserved for end-user post-build processing.
  2. The PhysicalPath attribute passed to the AspNetCompiler task needs to be updated to a value that will work both locally and on the build server.

Fortunately, both of these issues are easily resolved. In the example below, I’ve introduced a new BuildViews target that will only execute if the MvcBuildViews property is set to true. It also takes advantage of a new feature in MSBuild 4.0 that simplifies the task of injecting a custom target into the dependency chain. The AfterTargets attribute tells MSBuild to execute this target after the Build target has finished. For the PhysicalPath attribute on the AspNetCompiler task, I’ve used WebProjectOutputDir which is defined in Microsoft.WebApplication.targets as a location that resolves correctly for builds in both Visual Studio and TFS.

 <Target Name="BuildViews" Condition="'$(MvcBuildViews)'=='true'" AfterTargets="Build">
  <Message Importance="normal" Text="Precompiling views" />
  <AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>

Note that after adding this custom target at an appropriate location in your web application’s project (.csproj) file (I’d recommend placing it just above the AfterBuild target, you’ll want to comment out the AfterBuild target.

Now, if you have an error in one of your views, it will show up as a compile-time error in Visual Studio and in your TFS builds. If you don’t want to take the performance hit of the additional compilation step, you can conditionally set the MvcBuildViews property to true for a specific configuration only (e.g. Release).