ASP.NET Core - Precompiling razor views

Improving the startup time of your website is important. There are many reasons for a website to take times to get ready: loading lots of dll, loading data, compiling views, etc. In this post, I'll explain how to reduce the startup time by precompiling razor views.

The view precompilation is a build step that generates a DLL from the cshtml. Instead of doing the view compilation at runtime the first time you hit a view, the compilation is done at build time. This means, the startup time of your website will be reduced.

Of course, you don't want the precompilation process to be hard to set up. So, Microsoft has made a NuGet package to precompile your views (GitHub). Let's see how to use it!

How to enable razor view precompilation

  1. Add the NuGet package Microsoft.AspNetCore.Mvc.Razor.ViewCompilation
  2. Edit the csproj file and add <MvcRazorCompileOnPublish>true</MvcRazorCompileOnPublish>
<Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp1.1</TargetFramework>
    <PreserveCompilationContext>true</PreserveCompilationContext>
    <MvcRazorCompileOnPublish>true</MvcRazorCompileOnPublish>
  </PropertyGroup>
  1. Publish your site
dotnet restore
dotnet publish -c release
  1. You must see a *.PrecompiledViews.dll file

ASP.NET Core Precompiled views dll

It's so easy 😃

What contains the PrecompiledViews dll

If you are curious, you may wonder what's in the dll. The dll contains a class per view. The class generated from a view contains some properties and one method: ExecuteAsync. This method calls the Write and WriteLiteral methods, plus some additional method calls for infrastructure support.

@model Page
@{ var page = Model; }
<div class="page">
    @Html.Raw(page.Content)
</div>
namespace AspNetCore
{
  public class _Views_Blog_Page_cshtml : RazorPage<Page>
  {
    [RazorInject]
    public IModelExpressionProvider ModelExpressionProvider { get; private set; }

    [RazorInject]
    public IUrlHelper Url { get; private set; }

    [RazorInject]
    public IViewComponentHelper Component { get; private set; }

    [RazorInject]
    public IJsonHelper Json { get; private set; }

    [RazorInject]
    public IHtmlHelper<Page> Html { get; private set; }

    public override async Task ExecuteAsync()
    {
      Page model = this.Model;
      this.BeginContext(91, 24, true);
      this.WriteLiteral((object) "<div class=\"page\">\r\n    ");
      this.EndContext();
      this.BeginContext(116, 22, false);
      this.Write((object) this.Html.Raw(model.Content));
      this.EndContext();
      this.BeginContext(138, 8, true);
      this.WriteLiteral((object) "\r\n</div>");
      this.EndContext();
    }
  }
}

The code can be more complex if your view contains more code or tag helpers.

Conclusion

Depending on the number of pages available in your website, you will see a reduction of the startup time. Even if you don't have a lot of pages, it's so easy to integrate that you should enable it anyway 😃

Leave a reply