If you've recently started a new ASP.NET Core 2.1 project, or upgraded an existing project, and your username contains a space, you may find your builds and compilation is very slow. This post explains how to dramatically speed things up.

Razor has received some new features in ASP.NET Core 2.1. The big one is that you can now create Razor class libraries that let you use Razor to create reusable components, views, and pages for your web applications.

To enable this, the building, packaging, and publishing of Razor files in ASP.NET Core projects has been standardized with a new Razor SDK.

Changes in Razor SDK Build Process

One significant difference between the Razor SDK build process and the previous method, is that the Razor SDK has RazorCompileOnBuild set to true by default.

This means that all your Razor files are now compiled and output as an assembly at build-time, instead of being compiled on-demand as they were previously.

This is definitely what you want in production as it speeds up the start-up of your websites and is something that I enabled manually in previous versions of ASP.NET Core.

Furthermore, it means you get compile time error checking of your Razor files during development. Compile time errors are much better than runtime errors.

Problem

Unfortunately, RazorCompileOnBuild seems to slow compilation down massively.

In a new ASP.NET Core 2.1 project with only a handful of Razor view files, I found compilation was taking 48 seconds!

After disabling RazorCompileOnBuild, compilation was instantaneous (mere milliseconds).

After some deep digging, it turns out, the issue is that the Razor Build Server does not like usernames containing spaces.

Solution

To speed up your build, right click on your project in Solution Explorer, and select Edit [ProjectName].csproj.

Then add <UseRazorBuildServer>false</UseRazorBuildServer> as a child of the PropertyGroup element as follows:

<PropertyGroup>
  <TargetFramework>netcoreapp2.1</TargetFramework>
  <UseRazorBuildServer>false</UseRazorBuildServer>
</PropertyGroup>

That's it. You should now have lightning fast compilations again.

Furthermore, Razor is still compiled ahead of time using the in-process compiler, so you still get compile time error checking and fast site start-up in production.

How to Debug

In case you run into similar problems and want to debug them yourself, here is how I debugged it.

Initial Investigation

I didn't believe that compiling should be this slow.

I started by doing some searches on Google, then some searches on the relevant GitHub repositories, and finally, when those turned up nothing, I went to read the release notes for ASP.NET Core 2.1.

In the release notes, I found the changes to Razor, including RazorCompileOnBuild.

My first step was to disable RazorCompileOnBuild and sure enough, compilation was fast again.

At this stage, I suspected that something is failing during Razor compilation and that it ends up waiting for some timeout to occur.

I then checked with David Fowler from the ASP.NET team at Microsoft to see if this was a known issue, but David hadn't seen it before.

Simple Repro

To dig deeper, I started by creating a simple repro of the issue.

I found that I could consistently reproduce the issue on Visual Studio 2017 v15.7.4 as follows:

  1. In Visual Studio, open File menu, select New Project.
  2. Open .NET Core tab and select ASP.NET Core Web Application.
  3. Select the Web Application (Model-View-Controller) template and ASP.NET Core 2.1.
  4. Right Click on Project and select Rebuild

At this point, compilation takes 45 seconds and is the same on every rebuild.

If I modified the csproj and added <RazorCompileOnBuild>false</RazorCompileOnBuild>, rebuilds became very quick.

dotnet Command Line

As a further experiment, I took the same project and tried compiling from the command line by executing the following commands:

  1. dotnet clean
  2. dotnet build

The command line build process takes 44.81 seconds according to its own timer. I ran this process three times and the times were consistent: 44.81, 44.81, and 44.84 seconds.

After disabling Razor compilation on build, I ran the command line build process three more times and the times were consistently fast: 2.13, 2.10, and 2.12 seconds.

Digging Deeper

I then ran dotnet build -v diag to check out the diagnostics. It shows 20888ms in RazorCoreGenerate and 21455ms in ResolveTagHelperRazorGenerateInputs, which correspond to the task times for RazorGenerate and RazorTagHelper.

I then went looking through the logs. The only suspicious lines that I found were:

Server execution failed with response Rejected. For more info, check the server log file in the location specified by the RAZORBUILDSERVER_LOG environment variable. (TaskId:60)

Fallback to in-process execution.

Server execution failed with response Rejected. For more info, check the server log file in the location specified by the RAZORBUILDSERVER_LOG environment variable. (TaskId:66)

Fallback to in-process execution. (TaskId:66)

So, the issue seems to be with the Razor Build Server failing and then resolving itself with the fallback after a 20 second time out. It does this twice, and hence why I was getting >40 second build times.

At this point, I decided to try disabling the Razor Build Server using <UseRazorBuildServer>false</UseRazorBuildServer> instead of disabling the Razor compilation entirely with RazorCompileOnBuild.

Success! The builds are now fast with Razor compilation. The issue is with the Razor Build Server.

Known Issue

At this point, I had completed my investigations and was ready to file a GitHub issue.

With my new found knowledge, I started by conducting some new searches and turned up this issue.

It seems that others have experienced the same problem and have already taken it a little further, to discover that the issue occurs when you have a space in your Windows username.

The fix has already been committed and the patch will make its way into a future ASP.NET Core 2.1 release.

In the interim, you can work around the issue by disabling the build server using <UseRazorBuildServer>false</UseRazorBuildServer> as described earlier in this post.