'CompilationFailedException during runtime compilation of Razor from AspNetCore TestHost

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.



Solution 1:[1]

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>

Solution 2:[2]

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Groo
Solution 2 Groo