ASP.NET Core - Precompiling razor views

 
 
  • Gérald Barré

Improving the startup time of your website is important. Websites can be slow to start for many reasons: loading DLLs, loading data, compiling views, and more. In this post, I'll explain how to reduce startup time by precompiling Razor views.

View precompilation is a build step that compiles .cshtml files into a DLL. Instead of compiling views at runtime the first time they are requested, compilation happens at build time, reducing the startup time of your website.

Microsoft provides a NuGet package to handle this automatically (GitHub). Here's 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>

    csproj (MSBuild project file)
    <Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk.Web">
      <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>netcoreapp1.1</TargetFramework>
        <PreserveCompilationContext>true</PreserveCompilationContext>
        <MvcRazorCompileOnPublish>true</MvcRazorCompileOnPublish>
      </PropertyGroup>
  3. Publish your site

    Shell
    dotnet restore
    dotnet publish -c release
  4. You should see a *.PrecompiledViews.dll file in the output.

    ASP.NET Core Precompiled views dllASP.NET Core Precompiled views dll

That's all it takes!

#What the PrecompiledViews DLL contains

The DLL contains one class per view. Each generated class includes some properties and one method: ExecuteAsync. This method calls Write and WriteLiteral, along with additional calls for infrastructure support.

HTML
@model Page
@{ var page = Model; }
<div class="page">
    @Html.Raw(page.Content)
</div>
C#
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 generated code can be more complex if your view uses more logic or tag helpers.

#Conclusion

Depending on the number of views in your website, you will notice a meaningful reduction in startup time. Even if you don't have many views, the integration is simple enough that you should enable it regardless.

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?