Precompiled Razor Views

Sun, Feb 2, 2020 in Development , Sitecore using tags Sitecore , Performance , ASP.NET MVC , Razor

Precompiled Razor Views - Sitecore 9 - Issue 192470 & 119596

There are already some posts on this topic of using Precompiled Razor Views, so I won’t duplicate that content.

Kam’s blog - Precompiled Views supported by Sitecore since Sitecore 8.2

Jeremy’s blog - possible side affect of Precompiled Views

Chris’s blog - Slow compilation when using MVC areas/Helix folder structure

Matt’s blog - ensuring your razor view paths are unique

And probably others, but these one’s stood out to me, and relevant for this post.

Read these articles first, I’ll wait.

Quick summary

So you’ve read the above articles, here’s a quick recap:

  • we know Precompiled Razor Views are supported by Sitecore since Sitecore 8.2.

  • We want to measure/benchmark before and after applying this performance fix, how else would you know it’s had the desired effect. And watch out for this Disk IO calls, which seem to be occurring even if have the Sitecore setting disabled “UsePhysicalViewsIfNewer” which seems counterintuitive.

  • If you are following a MVC areas/Helix style structure with multiple folders for your views, it looks like using precompiled razor view is going to be really beneficial in your case.

  • Ensure you are using unique MVC area/Helix folder names & unique razor file paths across projects to avoid issues, just like you would if they were physical files in one project.

Ok - I’ve read the above articles, what more are you writing about?

Well first I want to draw attention to “UsePhysicalViewsIfNewer” setting.

Check in your Sitecore setup what this value is set to.

If for example you have received a Sitecore Support patch/hotfix with a new .cshtml file

E.g. Issue 214239 for “\sitecore\shell\client\Business Components Library\version 2\Layouts\Renderings\ListsAndGrids\Grid.cshtml”

Which includes a config patch which changes “UsePhysicalViewsIfNewer” to true to load the .cshtml file with the fix, rather than the precompiled view in the assembly.

Those disk IO calls which seem to occur either way (more on that later), are defiantly going to occur if you tell it to.

This also explains the guidance you might see where the .cshtml file last modified data must be more recent than the .dll file, otherwise it wouldn’t be loaded.

So possibly a Sitecore Support patch to a .cshtml file might undo some of the performance gains we are trying to achieve. As with everything measure.

If you don’t have “UsePhysicalViewsIfNewer” set to true, can skip this next section, and skip to the hotfix, otherwise read on.

I want a support fix and performance settings

Disclaimer, I’ve only tried this on a dev machine. And not completed benchmarking/testing

Maybe if you are only worried about the Sitecore MVC .cshtml hotfix for Content Authoring, and only want the compilation benefit on Content Delivery so could change this setting “UsePhysicalViewsIfNewer” to true just for Authoring?

However, if you want to have the compilation benefit on Content Authoring as well as the MVC .cshtml fix. One possible way could be to create a new project named “ZZZZZ…” with Razor compilation enabled and place the fixed .cshtml file in that project with the matching path of the file you want to override from Sitecore.

And add to the pre-compilation configuration a reference to this new assembly.

<configuration xmlns:patch"http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <mvc>
            <precompilation>
                <assemblies>
                    <assemblyIdentity name="ZZZZZ......AssemblyName" />
                </assemblies>
            </precompilation>
        </mvc>
    </sitecore>
</configuration>

Why call the project ZZZZZ….

We are going to turn the bug from Matt’s blog - ensuring your razor view paths are unique into a solution.

“The problem becomes apparent when two assemblies have the same generated virtual view path, the assembly that comes alphabetically last wins out and overwrites the value corresponding to the virtual path key string in the _views dictionary.”

We are going to deliberately overwrite the Sitecore Pre-compiled view, with the fixed Pre-compiled view, but naming the assembly with the fixed .cshtml file with a name Alphabetically after the original Sitecore Assembly.

If this is a good solution without drawback, perhaps support could do this, to save us the time of doing it ourselves?

Or actually Sitecore could re-issue the original .dll with the fixed Razor view, so don’t need to overwrite it.

And now with the new Hotfix only model, they probably would do this.

Still this might be relevant for an existing patch you have, prior to the new Hotfix model.

Best of both

With this technique, can leave “UsePhysicalViewsIfNewer” set to false, and override a Sitecore precompiled view.

More testing required to verify this works without drawback.

Great so I’ve definitely got “UsePhysicalViewsIfNewer” set to false, what’s this Hotfix

Disclaimer, I’ve only tried this on a dev machine. And not completed benchmarking/testing

Going back to Jeremy’s blog possible side affect of Precompiled Views. If look at the comments section, can see I enquired about if this was still an issue in Sitecore 9, and had a little more success from Sitecore Support who confirmed this was a bug in Sitecore and not the RazorGenerator project.

Looking at the contents of Sitecore.Support.192470.dll, can see it removes the first MVC view engine from the list, I asked Sitecore Support why and got this answer:

The setting UsePhysicalViewsIfNewer works as expected for all precompiled views except the ones that come from a couple of SPEAK dlls (Sitecore.Speak.Web.dll and Sitecore.Speak.Components.Web.dll). The UsePhysicalViewsIfNewer setting value is used to initialize PrecompiledViewAssembly objects which will be a part of the RazorGenerator.Mvc.CompositePrecompiledMvcEngine. Sitecore initializes an instance of Sitecore.Mvc.SitecoreRazorViewEngine derived from the CompositePrecompiledMvcEngine. And further the instantiated engine is encapsulated in the Sitecore.Mvc.InstrumentendViewEngine type.

So far so good.

The problem is that there is an extra PrecompiledMvcEngine instance that comes from SPEAK. This particular engine instance ignores the UsePhysicalViewsIfNewer setting. The engine instance gets placed at the very first position of the ViewEngines.Engines list. And the issue happens when this “incorrect” engine is taken via a FirstOrDefault LINQ func by Sitecore MVC. Please also notice that with the current architecture the only possible way to fix this is to remove the engine from the list.

This would seem to explain the issue Jeremy was having.

However I’ve yet to benchmark this solution, to see if solves the disk IO issue, but got a logical explanation from Sitecore Support.

I’m not sure if this is still an issue or fixed in later version of Sitecore after Sitecore 9. But a quick scan through the recent version release notes I can’t see either of the public reference numbers 192470 or 119596.

Summary

Disclaimer, I’ve only tried this on a dev machine. And not completed benchmarking/testing

If you are experiencing issues with Compiled Razor views and disk activity even with “UsePhysicalViewsIfNewer” set to false, perhaps check if this fix is applicable for the version of Sitecore you are on, and measure if it has the desired impact.

Measure Everything