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.

12 thoughts on “Precompiling MVC Views in ASP.NET Core with .csproj’s

  1. Thanks for this Scott, was just trying to remember what those elements were called.

    It’s worth noting that the Microsoft.AspNetCore.Mvc.Razor.ViewCompilation is a build-time only package, so you can avoid it appearing in your publish folder using PrivateAssets=”All”:

    Thanks again!

  2. Just for reference, when no view files in the project, or the view files are not marked as “Content” in the csproj project file (This is the “Build Action” in the properties dialog in Visual Studio), following errors can be reported:
    error CS0826: No best type found for implicitly-typed array
    error CS1503: Argument 1: cannot convert from ‘?[]’ to ‘System.Collections.Generic.IReadOnlyList’

    To solve this problem, either change the build action of your view files to “Content” if you have view files to compile or remove the “MvcRazorCompileOnPublish” property if you don’t have any view files.

  3. I hope nobody here uses pre-compiled views and publish in local development? I have not seen yet a blog post with tips and tricks for local deploy and development 🙂

    • Hi Janus! Not 100% sure what you mean by local deploy? When you dev locally, your views are not pre-compiled, they are just compiled on the fly. That way you don’t have to pay the slightly heavier compile time cost and you can iterate faster.

      Pre-compilation in ASP.NET Core doesn’t actually happen until you right-click publish in VS or do dotnet publish at the command line. So locally that should never happen, even when running ASP.NET Core behind IIS Express.

      Thanks for the comment!

      • Hi Scott
        Thank you for your fast reply.
        I read your blog post as publish in the sense of always hit the button even when developing in local environment i.e. deploy to local IIS.
        Could you tell a little about how you would work in a local environment and using an IIS, no IIS Express.

        Cheers
        Janus

      • Gotcha. To be honest – I just never have had a need to do that. What scenario do you have that you need full IIS and not IIS Express?

      • Hi Scott
        Content Management Systems, in example Sitecore. Though it is possible that it can run with IIS Express, but in those many years I’ve worked with it, I haven’t seen it yet 🙂

        I guess it is just one of those special cases.

        Anyway.. your posting is still very interesting.

        Have a great day.

Leave a Reply