CompilationFailedException during runtime compilation of Razor from AspNetCore TestHost

1.8k Views Asked by At

I have ASP.Net MVC application, one part of it is compiling razor views to string. The code is very similar to this example: https://long2know.com/2017/08/rendering-and-emailing-embedded-razor-views-with-net-core/

I registered Razor engine in Startup.cs in this way:

var viewAssembly = typeof(HtmlGeneratorService).GetTypeInfo().Assembly;
var fileProvider = new EmbeddedFileProvider(
    viewAssembly,
    "ApplicationServices.Widgets.Html.Templates");

services.Configure<MvcRazorRuntimeCompilationOptions>(options => {
    options.FileProviders.Clear();
    options.FileProviders.Add(fileProvider);
});

services.AddRazorPages().AddRazorRuntimeCompilation();

In test project i have this setup:

var builder = new HostBuilder()
    .ConfigureWebHost(webHost =>
    {
        webHost.UseTestServer();
        webHost.UseStartup<Startup>();
    });

var host = await builder.StartAsync();

HttpClient = host.GetTestClient();

But when i call my endpoint using this HttpClient, IRazorViewEngine.GetView starts to throw strange exceptions:

Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.CompilationFailedException: 'One or more compilation failures occurred:
rnouw0xu.21w(4,41): error CS0234: The type or namespace name 'Razor' does not exist in the namespace 'Microsoft.AspNetCore' (are you missing an assembly reference?)
rnouw0xu.21w(4,82): error CS0518: Predefined type 'System.Type' is not defined or imported
rnouw0xu.21w(4,110): error CS0518: Predefined type 'System.String' is not defined or imported
rnouw0xu.21w(4,127): error CS0518: Predefined type 'System.String' is not defined or imported
rnouw0xu.21w(8,11): error CS0246: The type or namespace name 'System' could not be found (are you missing a using directive or an assembly reference?)
rnouw0xu.21w(9,11): error CS0246: The type or namespace name 'System' could not be found (are you missing a using directive or an assembly reference?)
rnouw0xu.21w(10,11): error CS0246: The type or namespace name 'System' could not be found (are you missing a using directive or an assembly reference?)
rnouw0xu.21w(11,11): error CS0246: The type or namespace name 'System' could not be found (are you missing a using directive or an assembly reference?)
rnouw0xu.21w(13,36): error CS0234: The type or namespace name 'Rendering' does not exist in the namespace 'Microsoft.AspNetCore.Mvc' (are you missing an assembly reference?)
rnouw0xu.21w(14,36): error CS0234: The type or namespace name 'ViewFeatures' does not exist in the namespace 'Microsoft.AspNetCore.Mvc' (are you missing an assembly reference?)
rnouw0xu.21w(29,35): error CS0234: The type or namespace name 'Razor' does not exist in the namespace 'Microsoft.AspNetCore' (are you missing an assembly reference?)
rnouw0xu.21w(29,78): error CS0518: Predefined type 'System.String' is not defined or imported
rnouw0xu.21w(29,87): error CS0518: Predefined type 'System.String' is not defined or imported

I trided to fix this erros in many different ways bul it looks like I got stuck here.

2

There are 2 best solutions below

1
On BEST ANSWER

Looks like i found answer in this article: https://github.com/aspnet/Razor/issues/1212

I just added this code to my test.csproj file:

<Target Name="CopyDepsFiles" AfterTargets="Build" Condition="'$(TargetFramework)'!=''">

   <ItemGroup>
    <DepsFilePaths Include="$([System.IO.Path]::ChangeExtension('%(_ResolvedProjectReferencePaths.FullPath)', '.deps.json'))" />
   </ItemGroup>

   <Copy SourceFiles="%(DepsFilePaths.FullPath)" DestinationFolder="$(OutputPath)" Condition="Exists('%(DepsFilePaths.FullPath)')" />

</Target>
1
On

Just extending the approved answer a bit since the suggested fix didn't work for me when upgrading to .NET 6, runtime was still unable to locate the assemblies until I explicitly added a AddApplicationPart(assembly) call to the IMvcBuilder.

Also a minor simplification is to pass the options lambda to the AddRazordRuntimeCompilation call, in which case services.Configure<MvcRazorRuntimeCompilationOptions> can be removed.

It might have failed due to several reasons (this was a Windows service project, views were in a separate dll), but anyway this is a compilation of various fixes I gathered from the web:

// this part is needed if you don't have a valid IWebHostEnvironment
// service registered, in which case you need to create your own dummy
// implementation because Razor needs IWebHostEnvironment.ApplicationName
// (it should return Assembly.GetEntryAssembly().GetName().Name, and you 
// can leave other properties null)
services.AddSingleton<IWebHostEnvironment, DummyHostingEnvironment>();
services.AddSingleton<IHostEnvironment, DummyHostingEnvironment>();

// this also needs to be registered as two separate dependencies
// if you're getting "Unable to resolve service for type
// 'System.Diagnostics.DiagnosticListener'"
// (see https://github.com/dotnet/aspnetcore/issues/14544)
var diagnosticSource = new DiagnosticListener("Microsoft.AspNetCore");
services.AddSingleton<DiagnosticSource>(diagnosticSource);
services.AddSingleton<DiagnosticListener>(diagnosticSource);

// make sure you specify correct assemblies, in case the views
// are in a separate dll
var assemblyWithTemplates = ...;
var assemblyReferencingTheRazorPackage = ...;

services
    .AddRazorPages()
    .AddApplicationPart(assemblyReferencingTheRazorPackage ) // <-- added this
    .AddRazorRuntimeCompilation(options =>
    {
        // important to clear because some of them are null
        options.FileProviders.Clear();

        // resolve views as embedded resources
        options.FileProviders.Add(new EmbeddedFileProvider(assemblyWithTemplates));
    });

And then also add the code from the accepted answer to the .csproj file so that .deps files are properly copied.