0
votes

In ASP.NET Core 2.1, I have a DLL project (Microsoft.NET.Sdk) with Razor views for sharing common partial views like navigation, footer, ... with multiple applications. It was created like described here. So I have an ASP.NET Core 2.1 MVC app that includes those libraries and embeds partial views.

While this works, I noticed a large increase in the first page load time in this application. It took about 3 seconds for the main HTML document. Subsequent requests are pretty fast (~30-40ms). I tried removing all the partial view in the application that was embedded by the shared DLL. Now the first request is much faster (about 300ms).

So it seems caused by loading Razor views from the shared DLL project. I tried pre-loading the assembly using different methods like this in AppStartup method (inspirated from this question):

GC.KeepAlive(typeof(Assets.AssetStartup));
System.Reflection.Assembly.Load("Assets"); 

No large difference in performance, it still took ~2.9 seconds on the first request.

How can I improve this?

I know about warming up from EntityFramework, for this reason, I had the idea to solve this problem in a similar way. But I'm not sure where it exactly slow down my app: Is it the loading of the assembly (and my pre-fetching attempts were wrong in some way), or is it still about Razor compilation?

Because I also know about performance improvements by pre-compiling Razor views. In ASP.NET Core 1, I manually enabled this and the first request was noticeably faster. Since Microsoft re-wrote this part and enabled pre-compilation by default, but my Razor DLL app doesn't generate a file called {AppName}.Views.dll (where MVC apps have them), I'm unsure if this may be the problem.

1
One important question. Are you experiencing this when you publish using a release configuration? Or are you experiencing this in development? - Dennis1679

1 Answers

0
votes

Some posts and also blog posts indicates that there are problems with pre-compilation on Razor lib projects. But I remembered from the .NET Core docs that MvcRazorCompileOnPublish is deprecated and removed in 3.0. It disables the new Razor SDK. This seems not a reliable solution to me.

So I created an empty Razor project and compared the csproj file to my lib project. It shows that it references Microsoft.NET.Sdk.Razor instead of Microsoft.NET.Sdk from my project (created as a regululr .NET core Core class library). After chaning the SDK, my Debug folder also contains a Assets.Views.dll file, which indicates pre-compilation.

It's also added by NuGet, which could be easily verified by opening the .nupkg file with 7zip. The lib/netcoreapp2.1 folder now contains the .Views.dll, too.

This solution improves the first request rendering time from ~3 seconds to 1.1 seconds. So much faster, but still a lot slower compared to 300ms without the external DLL. I consider this as part of the solution, but it seems there are additional bottlenecks that slow down the first request. I'm open to additional ideas how to improve this last delay.

I currently include only four partial views from the Razor DLL. One has 108 lines of html with a bit of dynamic razor logic and two injected services. The others are much smaller and don't contain a lot of dynamic logic yet. I also tried not to include the larger partial view, no noticeable difference.

I can't belive that including four relatively small partial views from an external Razor lib delay the first request for 800ms. This is not the case when the view is locally in the same project.

Pre-Loading is not the problem

Found a blog entry from Rick Strahl with a snippet that lists all loaded assemblys:

var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies) {
    Console.WriteLine(assembly.GetName());
}

I ran this in ConfigureService of my app that consumes the Razor DLL. It shows both assemblys from my Razor DLL file (Assets and Assets.Views) were included. I think this is the case because I fetch the assembly info to pass it as a EmbeddedFileProvider to RazorViewEngineOptions. However, it explains why my pre-loading attemps doesn't have any effect on the page generation time.