Generating precompiled Razor views in ASP.NET Core is easy. However, there are a few things you need to know so you don’t end up scratching your head for days.
Generating Precompiled Razor Views on Build
The NuGet package Microsoft.AspNetCore.Mvc.Razor.ViewCompilation allows you to generate precompiled Razor views. The documentation shows how to enable precompilation upon project publishing in the CSPROJ file:
<PropertyGroup> <MvcRazorCompileOnPublish>true</MvcRazorCompileOnPublish> </PropertyGroup>
Additionally, you can generate precompiled Razor views on each build. The “old” repository on GitHub shows that adding the following target to the CSPROJ file will do the trick:
<Target Name="PrecompileRazorViews" AfterTargets="Build" DependsOnTargets="MvcRazorPrecompile" />
Both of the methods above result in an assembly located inside the obj directory. The assembly will have the same output name as the project, but with the suffix .PrecompiledViews.dll. You can customize the precompilation target and copy the generated assembly into your project’s output directory by replacing the above target with something like this:
<Target Name="CustomPrecompileViews" AfterTargets="Build"> <CallTarget Targets="MvcRazorPrecompile" /> <Copy SourceFiles="$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFramework)\$(TargetName).PrecompiledViews.dll" DestinationFolder="$(OutputPath)" /> </Target>
Common errors
The text above and the documentation suggest that view precompilation is a straight forward process. Unfortunately, when I first tried to do this, there were lots of errors and a solution was hard to find.
The compiled view assembly is missing
If you use the PrecompileRazorViews target instead of a custom target, you won’t notice this error directly. If you try the custom target from above, you’ll get an error similar to Could not copy the file "obj\Debug\netstandard2.0\MyWebComponents.PrecompiledViews.dll" because it was not found.
The best thing to do here is to build the project from command line and enable detailed output: dotnet build MyWebComponents.csproj -v d
In my case, I found two output lines that showed me what my problem was:
Target “_RunForCore” skipped, due to false condition; (‘$(TargetFrameworkIdentifier)’==’.NETCoreApp’ AND ‘$(RuntimeIdentifier)’==”) was evaluated as (‘.NETStandard’==’.NETCoreApp’ AND ”==”).
Target “_RunForDesktop” skipped, due to false condition; (‘$(TargetFrameworkIdentifier)’==’.NETFramework’) was evaluated as (‘.NETStandard’==’.NETFramework’).
So apparently, view precompilation only works for the target frameworks .NETFramework and .NETCoreApp. The target framework of my project was set to netstandard2.0. In my CSPROJ file, I changed the target framework to dotnetcoreapp2.0:
<PropertyGroup> <TargetFramework>netcoreapp2.0</TargetFramework> </PropertyGroup>
After that, this error went away.
The command “dotnet” … exited with code -2147450733
What does this even mean? Again, run build the project from command line with detailed output and behold the true meaning behind this: The specified runtimeconfig.json … does not exist.
This error essentially means that your project is not an executable. So technically, precompiled views are not really supported
in class libraries. Fortunately, that is not a problem, since we can still produce a DLL. It just needs a main entry point. Merely adding a program.cs won’t do here, so read on to the next error for the fix.
The type or namespace name ‘System’ could not be found
Wait what? This missing namespace is the basic namespace in any .NET framework\SDK\standard, how could it be missing? I’m not an expert on the internals of the compilation process for Razor views. However, the problem here was that the project containing the views to be precompiled must be a web project. The Addition of the word “.Web” made all my problems disappear. This is what my CSPROJ file looked before:
<Project Sdk="Microsoft.NET.Sdk">...</Project>
This is what my CSPROJ file looked afterwards:
<Project Sdk="Microsoft.NET.Sdk.Web">...</Project>
Program does not contain a static ‘Main’ method suitable for an entry point
At last, an error message that speaks for itself. So just add a program.cs with a minimal implementation and you’re good to go.
using System; namespace MyWebComponents { public class Program { public static void Main(string[] args) { throw new NotSupportedException("This assembly cannot be started directly."); } } }
using System; namespace MyWebComponents { public class Program { public static void Main(string[] args) { throw new NotSupportedException("This assembly cannot be started directly."); } } }
After fixing all of the problems above, I had an assembly containing my precompiled views in the output folder.
Recap
To sum up, here’s what I did to get my precompiled views running:
- Set the target framework to netcoreapp2.0.
- Set the project SDK to Microsoft.NET.Sdk.Web.
- Add a main entry point (static Main method).
Thank you so much for this blog! Been struggling with it for a while. A beer on me 🙂
Super helpful! You have a typo here:
$(TargetFramework\$
(Missing the closing paren)
Thank you, Mike. I updated the paren.