Precompiling MVC Views in ASP.NET Core with .csproj’s

Update 8/29/2017: 

When running ASP.NET Core 2:

  • Using .NET Core 2
    • You no longer have to do anything.  The app will precompile by default on publish.
  • Using full .NET Framework
    • The only thing you need is the package reference below.


<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="2.0.0" PrivateAssets="All" />

 

Below is the original post for ASP.NET Core 1

 

I didn’t see this documented on docs.asp.net yet for .csproj style projects, so I thought I’d blog about it, as it’s something I do on every project.

tldr;

Add this to your .csproj file:

2017-03-10_13-02-21.png

Alternatively, you can check out this GitHub commit that just adds pre-compiled views here.

Also – if you’re running on .NET Core and not Full Framework, then make sure your TargetFramework is netcoreapp1.1+ and not netcoreapp1.0.

What does precompiling views do?

Without precompiling your views, none of your views are compiled until they are requested for the first time.  This results in an extra few seconds of wait time for the end user.  Instead, most developers want to take a little bit of extra time in the publish step to precompile their views, so that users don’t have to eat that performance hit on the first request on every page.

Another benefit of precompiling your views is if there is a C# error on one of your .cshtml Razor pages (such as you changed the name of a property on your View Model and the rename didn’t update your .cshtml file), you will find out up front when you go to publish (via VS or a build server) rather than at run time when a user goes to request that page.

So now that we’ve seen what it does, let’s look at what it used to be in project.json.

What did it look like in project.json?

In project.json land, the pre-compiling of Views was a little verbose.  You had to do the following:

  1. Add a reference to “Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design” under the “dependencies” section
  2. Add a reference to “Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Tools” under the tools section.
  3. Under buildOptions, set “preserveCompilationContext”to true
  4. And finally you had to add a fairly verbose post-publish script to actually use the pre-compile tool.All of that looked like this (removing the rest of the project.json for brevity):


{
"dependencies": {
"Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Design": {
"version": "1.1.0-preview4-final",
"type": "build"
}
},
"tools": {
"Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Tools": "1.1.0-preview4-final"
},
"buildOptions": {
"preserveCompilationContext": true
},
"scripts": {
"postpublish": [
"dotnet razor-precompile –configuration %publish:Configuration% –framework %publish:TargetFramework% –output-path %publish:OutputPath% %publish:ProjectPath%"
]
}
}

view raw

project.json

hosted with ❤ by GitHub

This then generates a .PrecompiledViews.dll in the root of your publish directory.  You will have no refs folder as well.

How does it look in .csproj?

In project.json, it was pretty verbose.  So what does it look like in .csproj?  It’s much cleaner.

  1. Add <MvcRazorCompileOnPublish>true</MvcRazorCompileOnPublish>
  2. Add <PackageReference Include=Microsoft.AspNetCore.Mvc.Razor.ViewCompilation Version=1.1.0 />

 

So the final output of a .csproj might look a little something like this.  Note lines 5,6, and 32.


<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<MvcRazorCompileOnPublish>true</MvcRazorCompileOnPublish>
<PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>
<PropertyGroup>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
</PropertyGroup>
<PropertyGroup>
<UserSecretsId>aspnet-WebApplication2-59d1bc3a-42e0-4a81-940b-ce5e3aba9dba</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="1.1.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="1.1.0" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="1.0.0" />
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0" />
</ItemGroup>
</Project>

This will still generate a .PrecompiledViews.dll in the root of your publish directory and you will still have no refs folder.

This simplicity in order to get precompiled views is yet another win for the .csproj style ASP.NET Core projects over project.json.