OdeToCode IC Logo

Spying on Razor View Compilation

Wednesday, February 3, 2016

Sometimes the best way to understand code is to compile and execute the code. In this scenario I needed to understand a bit more about view compilation in ASP.NET Core. Here is a simple spy to track the file information and compilation results for each razor view.

public class RazorCompilationServiceSpy : IRazorCompilationService
{
    private IRazorCompilationService _inner;
    private IList<CompileEntry> _log;

    public RazorCompilationServiceSpy(ICompilationService compilationService,
                                      IMvcRazorHost razorHost, 
                                      IOptions<RazorViewEngineOptions> options)
    {
        _inner = new RazorCompilationService(compilationService, razorHost, options);
        _log = new List<CompileEntry>();
    }

    public CompilationResult Compile(RelativeFileInfo fileInfo)
    {
        var result = _inner.Compile(fileInfo);
        _log.Add(new CompileEntry { FileInfo = fileInfo, Result = result });
        return result;
    }

    public IEnumerable<CompileEntry> CompilationLog
    {
        get
        {
            return _log;
        }
    }

    public class CompileEntry
    {
        public RelativeFileInfo FileInfo { get; set; }
        public CompilationResult Result { get; set; }
    }
}

The easiest way to grab information from the class is to register the class as a singleton during Startup::Configure.

 services.AddSingleton<IRazorCompilationService, RazorCompilationServiceSpy>();

Note that the real RazorCompilationService runs as a transient service in a typical ASP.NET application, but for this simple experiment we are ignoring all the terrible threading problems that might arise using singletons and regular collection classes like a List<T>.

Inside a Razor view we can use the following code to see the compilation results.

@using Microsoft.AspNet.Mvc.Razor.Compilation;

@inject IRazorCompilationService CompilationSpy

@functions {
    public RazorCompilationServiceSpy GetCompilationSpy()
    {
        return (RazorCompilationServiceSpy)CompilationSpy;
    }
}

<section>
    <h3>Compilation Results</h3>

    @foreach (var result in GetCompilationSpy().CompilationLog)
    {
        <h4>@result.FileInfo.RelativePath</h4>
        <pre>@result.Result.CompiledContent</pre>

    }

</section>

Which yields output like the following:

image