Liero
Liero

Reputation: 27328

How do I find/render precompiled Razor View

Scenario:

It works fine but when publish the app with precompiled views, the FindView from the link above methods returns null.

Steps to reproduce:

  1. Download the aspnet/Entropy/samples/Mvc.RenderViewToString sample
  2. Publish and run it

How do I find and render precompiled view at runtime?

Upvotes: 1

Views: 1556

Answers (3)

Sergei
Sergei

Reputation: 177

I could not understand why, but in the console application, AddMvc() skips one configuration step - it does not add a provider that retrieves precompiled views from assembly.

In my case I use console app (net472) and Razor class library and put RazorViewToStringRenderer in this library.

To add the provider, we need to do the following

var viewAssembly = Assembly.Load(typeof(RazorViewToStringRenderer).Assembly.GetName().Name + ".Views");
var viewAssemblyPart = new CompiledRazorAssemblyPart(viewAssembly);
services.AddMvc().PartManager.ApplicationParts.Add(viewAssemblyPart);

After this steps RazorViewToStringRenderer works perfect.

Upvotes: 3

Tony Basallo
Tony Basallo

Reputation: 3074

I'm using the same code in a web project and I had to add the complete directory info to the GetView attempt. The FindView method works (without precompilation) with the View or CSHTML extension. But the GetView needs both and always works (with and without precompilation of views)

var getViewResult = _viewEngine
      .GetView(
          executingFilePath: null, 
          viewPath: $"/Views/{viewName}.cshtml", 
          isMainPage: true);

Upvotes: 1

CodeFuller
CodeFuller

Reputation: 31282

The easiest fix would be disabling of precompiled views during the publish. If it is an option, then just set MvcRazorCompileOnPublish to false in csproj file

<PropertyGroup>
  <TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
  <MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
</PropertyGroup>

If however you want to use precompiled views, then you need to make several fixes in ConfigureDefaultServices method.

First of all, move services.Configure<RazorViewEngineOptions> call after AddMvc(). Otherwise you override RazorViewEngineOptions configuration setup added by AddMvc() and it will not be filled with required data (the job performed by RazorViewEngineOptionsSetup).

After this fix basic rendering will work however partial views and layout will not be located by Razor Engine. To fix this, you need to add location format without controller name (/Views/{0}.cshtml) to RazorViewEngineOptions.ViewLocationFormats collection.

After described fixes rendering based on precompiled views works fine for me. Here is corrected ConfigureDefaultServices method:

private static void ConfigureDefaultServices(IServiceCollection services, string customApplicationBasePath)
{
    string applicationName;
    IFileProvider fileProvider;
    if (!string.IsNullOrEmpty(customApplicationBasePath))
    {
        applicationName = Path.GetFileName(customApplicationBasePath);
        fileProvider = new PhysicalFileProvider(customApplicationBasePath);
    }
    else
    {
        applicationName = Assembly.GetEntryAssembly().GetName().Name;
        fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());
    }

    services.AddSingleton<IHostingEnvironment>(new HostingEnvironment
    {
        ApplicationName = applicationName,
        WebRootFileProvider = fileProvider,
    });

    var diagnosticSource = new DiagnosticListener("Microsoft.AspNetCore");
    services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
    services.AddSingleton<DiagnosticSource>(diagnosticSource);
    services.AddLogging();
    services.AddTransient<RazorViewToStringRenderer>();
    services.Configure<RazorViewEngineOptions>(options =>
    {
        options.ViewLocationFormats.Add("/Views/{0}.cshtml");

        options.FileProviders.Clear();
        options.FileProviders.Add(fileProvider);
    });
    services.AddMvc();
}

Upvotes: 1

Related Questions