toddams / RazorLight

Template engine based on Microsoft's Razor parsing engine for .NET Core
Apache License 2.0
1.51k stars 261 forks source link

Error compiling: The type or namespace name 'Razor' does not exist in the namespace 'RazorLight'... #406

Open jbaumbach opened 3 years ago

jbaumbach commented 3 years ago

Describe the bug When attempting to compile a string template and a complex model, we get the error:

Failed to compile generated Razor template:
- (3:29) The type or namespace name 'Razor' does not exist in the namespace 'RazorLight' (are you missing an assembly reference?)

To Reproduce

Expected behavior

Information (please complete the following information):

Additional context Clearly the library is referenced - there would be no way to call the .CompileRenderStringAsync() method otherwise. And it does build and compile fine.

I tried these things:

  1. Added the <PropertyGroup> settings you mentioned (all of them) in the ASP.NET .csproj file. It didn't change anything - and also those might apply only to .Net Core?

I don't think this is an issue with your library necessarily - it's just unclear how to figure this out.

jzabroski commented 3 years ago

To Reproduce

  • .Net Framework 4.7.2
  • Project is an ASP.NET solution with .cshtml views, which work fine
  • Use a non-trivial string template with references to C# POCOs (a simple "Hello World" string works fine)

I need to dig into this a bit, but we started getting clever with how we handled different target frameworks in this commit: https://github.com/toddams/RazorLight/commit/d56fd12e5199c46cc6564cc0a1e6284e33ccdaf9 - so it is possible there is a bug for .NET Framework 4.7.2 since we're targeting netstandard2.0 since that commit. I've been thinking recently that may have been a mistake, but haven't had time to tinker with it.

To confirm, there are two .CompileRenderStringAsync() methods in RazorLight - one on the EngineHandler and one on RazorLightEngine. Which one are you using?

Can you post a quick repro, like this?

var engine = new RazorLightEngineBuilder()
    .UseEmbeddedResourcesProject(typeof(Program))
    .UseMemoryCachingProvider()
    .Build();

var model = new SchoolForAnts();
string result = await engine.CompileRenderStringAsync<SchoolForAnts>("SchoolForAnts", "@Model.Value", model);
jbaumbach commented 3 years ago

Sure thing, thanks so much for checking this out. Here's what we have so far.


public static string Execute(string content, object model)
{
    var engine = new RazorLightEngineBuilder()
        // We get NREs at .Build() w/o both of these lines
        // Note: this is a random object from the project that holds our POCOs - not the currently executing project
        // Note 2: this is an ASP.NET app so there's no Program object
        .UseEmbeddedResourcesProject(typeof(OurWebApp.Data.Order))
        .SetOperatingAssembly(typeof(OurWebApp.Data.Order).Assembly)
        .UseMemoryCachingProvider()
        .Build();
    //
    // Causes error: Failed to compile generated Razor template:
    //      -(3:29) The type or namespace name 'Razor' does not exist in the namespace 'RazorLight' (are you missing an assembly reference?)
    //
    string result = Task.Run(() => engine.CompileRenderStringAsync("templateKey", content, model)).Result;
}
jzabroski commented 3 years ago

@jbaumbach

I spent 4.5 hours today fixing the solution so that it explicitly covers .NET 4.7.2 in RazorLight.Tests.csproj #407

Can you please add a failing test once I merge this into master branch?

When I did the tests, only one failed after I added SetOperatingAssembly: Constructor_SetDefines. I am still not sure why this behavior is different in .NET Framework vs. NET Core, and I tried decompiling the libraries to figure it out, but nothing made sense as to why this happens.

jbaumbach commented 3 years ago

Great, thank you so much John. I will test as soon as you merge.

jzabroski commented 3 years ago

I'm a little stuck and I don't know why exactly this worked in Travis CI with Ubuntu Bionic but is failing on GitHub (also Ubuntu Bionic). It has something to do I think with shadow copied test assemblies not copying over the Assets/Files folder in order to run the FileSystemRazorProject tests. The error I get is:

[xUnit.net 00:00:04.8184942]     RazorLight.Tests.Integration.RendererCommonCasesTests.Should_Fail_When_Required_Section_Is_Missing [FAIL]
[xUnit.net 00:00:04.8198416]       System.IO.DirectoryNotFoundException : Root directory C:\Users\runneradmin\AppData\Local\Temp\c2fc666c-2964-4d75-8976-d1deed540e76\c2fc666c-2964-4d75-8976-d1deed540e76\assembly\dl3\a151d461\0504fbde_a2e2d601\Assets\Files not found

I forked Andrew Lock's blog-examples repo and set-up a similar GitHub action for his XunitTheoryTests folder, here: https://github.com/jzabroski/blog-examples/actions/runs/461816253 - and it all works fine. It's also annoying that most CI/CD tools don't display ITestOutputHelper output unless the test fails. The big difference between what he is doing and what I was doing is he is using Path.GetRelativePath (which is only available on .NET Core and .NET 5+).

It seems as if, if we're running on Windows, we should use Directory.GetCurrentDirectory(), but if we're running on Linux/Mac, we should use this.GetType().Assembly.Location. But my tragic flaw is I want to understand why this is so different.

jzabroski commented 3 years ago

Ugh - think I figured it out. https://github.com/dotnet/runtime/issues/29470 - Path.GetDirectoryName is dependent on which OS you run it on and what input format the path is in. The path must be compliant with the OS you run it on. But .NET renders Assembly.Location using Windows rules, I guess, and that value is not normalized to the OS.

jbaumbach commented 3 years ago

Well, that sounds pretty complicated but glad you got to the bottom of it. Let me know when build RazorLight 2.0.0-rc.4 is ready and I'll check it out.

joaolongo commented 3 years ago

Hey @jzabroski by any chance do you have an ETA of when the correction for this issue will be released?

jzabroski commented 3 years ago

@joaolongo As far as I'm aware there are no issues. I explicitly added test coverage. Please submit a PR for a failing test and I can fix it pretty quickly. The remaining problem is I get incomplete repros.

0Neji commented 3 years ago

Hi @jzabroski

I'm running in to a similar error message when trying to use RazorLight on an Umbraco project.

I've got a project you can check out at: https://github.com/0Neji/UmbracoRazorStaticHtml

To recreate the error:

  1. Checkout and run the solution in debug
  2. Place a breakpoint at line 51 in HtmlGeneration.cs - https://github.com/0Neji/UmbracoRazorStaticHtml/blob/main/UmbracoStaticHtml/UmbracoStaticHtml/Components/HtmlGeneration.cs#L51
  3. Go to the Umbraco login area: https://localhost:44324/umbraco
  4. Login with following details: Username: test@papermoon.io Password: @Test123456
  5. Click on "Home"
  6. Click on "Save and Publish"

I've also put together a short video of the process: https://papermoonio-my.sharepoint.com/:v:/g/personal/ben_papermoon_io/EWheNegHzelKq6uQNALB-2oBU4MhvtEBPBbkjzfzXUhJzQ

Thanks,

Ben

GiantCaiB commented 2 years ago

This issue caused by .SetOperatingAssembly(typeof(OurWebApp.Data.Order).Assembly)

If your model is dynamic object, it will give you above errors. Be careful if you just simply remove this, RazorLightEngine.Build() will try to get the Assembly Assembly assembly = operatingAssembly ?? Assembly.GetEntryAssembly(); RoslynCompilationService compilationService = new RoslynCompilationService(referenceManager, assembly);

This will cause null value exception if you run your code in unit test and cross projects.