0

This is a follow up on Cannot compile a Razor document. I get several errors while compiling a Razor content (not a file). The code is like this:

var content = File.ReadAllText("RazorComponent.razor");
var assemblyName = "RazorComponent";
var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location)!;
var document = RazorSourceDocument.Create(content, assemblyName + ".razor");
var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
var references = new MetadataReference[]
{
    MetadataReference.CreateFromFile(Assembly.GetExecutingAssembly().Location),
    MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
    MetadataReference.CreateFromFile(typeof(IComponent).Assembly.Location),
    MetadataReference.CreateFromFile(typeof(RazorCompiledItemAttribute).Assembly.Location),
    MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "mscorlib.dll")),
    MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.dll")),
    MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Core.dll")),
    MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll"))
};

var engine = RazorProjectEngine.Create(
    RazorConfiguration.Default,
    RazorProjectFileSystem.Create("."),
    builder =>
    {
        builder.ConfigureClass((doc, node) =>
        {
            node.BaseType = typeof(ComponentBase).FullName;
        });
    });

var codeDocument = engine.Process(source: document, fileKind: null, importSources: [], tagHelpers: []);

var csDocument = codeDocument.GetCSharpDocument();
var syntaxTree = CSharpSyntaxTree.ParseText(csDocument.GeneratedCode);
var compilation = CSharpCompilation.Create(assemblyName, [syntaxTree], references, options);

using var assemblyStrean = new MemoryStream();
var result = compilation.Emit(assemblyStrean);

This does not success result.Success is false and I get several errors:

error CS0115: 'Template.ExecuteAsync()': no suitable method found to override

error CS0103: The name 'WriteLiteral' does not exist in the current context

error CS0103: The name 'Write' does not exist in the current context

Inspecting the generated code, I see that the generated class is called Template, inherits from ComponentBase, and is trying to override an non-existing method called ExecuteAsync. Inside of it is where it's calling Write and WriteLiteral, which, of course, don't exist too.

What am I missing?

2
  • 1
    It looks like there are two entirely different steps here: 1) generating the code; 2) compiling the code. I would try to separate these out. Does the generated code look like you'd expect? If so, can you hard-code it in your question, reducing it to just what's required? If not, you probably don't need the compiling part. Commented Jul 4 at 11:58
  • Thanks, @JonSkeet. As I said, this works perfectly when loading .razor file (produces code that inherits from ComponentBase) but not so when I pass string contents (which can be the actual contents of that file). On the second case, it produces totally different code, with an ExecuteAsync override (that does not exist on the base class). Commented Jul 5 at 11:42

2 Answers 2

1

From what i have found out, those methods are specific to razor components (used in Blazor) rather than for Razor Pages. And what we trying to do is to use Razor Engine that can handle Razor Pages.

And ComponentBase class does not have suitable methods that occur in the compilation step (ExecuteAsync, WriteLiteral, etc.), as it's meant to be for Razor Components.

Instead you must specify:

var engine = RazorProjectEngine.Create(
    RazorConfiguration.Default,
    RazorProjectFileSystem.Create("."),
    builder =>
    {
        builder.ConfigureClass((doc, node) =>
        {
            // HERE CHANGE BASE TYPE
            node.BaseType = typeof(RazorPage).FullName;
        });
    });

This will throw error that Mvc namespace is missing, so we need to add the reference:

var references = new MetadataReference[]
{
    MetadataReference.CreateFromFile(Assembly.GetExecutingAssembly().Location),
    MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
    MetadataReference.CreateFromFile(typeof(IComponent).Assembly.Location),
    MetadataReference.CreateFromFile(typeof(RazorCompiledItemAttribute).Assembly.Location),
    MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "mscorlib.dll")),
    MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.dll")),
    MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Core.dll")),
    MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll")),
    // HERE NEW REFERENCE TO MVC
    MetadataReference.CreateFromFile(typeof(RazorPage).Assembly.Location),
};

After that compilation result was successful.

I also think you can simplify references to those:

var references = new MetadataReference[]
{
    MetadataReference.CreateFromFile(Assembly.GetExecutingAssembly().Location),
    MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
    MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll")),
    MetadataReference.CreateFromFile(typeof(RazorPage).Assembly.Location),
};

I also have found recommendations to use RazorLight nuget library for that task.

Digging the source code for Razor

In RazorPage page class we can see that it inherits RazorPageBase which defines ExecuteAsync, Write and WriteLiteral methods.

On the other hand, we see that ComponentBase does not define any of these methods.

Please see the .NET fiddle

Sign up to request clarification or add additional context in comments.

12 Comments

I even get more errors this way... like "error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'". No luck.
Maybe try adding reference to correct libraries? This one sounds like mismatch in version of mscrolib i guess. I got it running correctly, but i am using single project with .net 10
Thanks, this is not the big problem, it’s how it is generating “wrong” code.
I dont get that. It generates correct code and i confirmed it with my code
Very surprised to hear that... this code doesn't generate valid code on my machine... care to send a gist of yours? Thanks.
I have used exact code provided in the answer :)
I'm sorry, but that is not possible... this code fails: gist.github.com/rjperes/f589ec172960a11c5c17448f34adbad0
Happy that you got it working :) if you find the answer helpful, you should consider upvoting and/or accepting it
Now I can't get it to work with this content: @ if (string.IsNullOrWhiteSpace(Message)) { <p>No message provided</p> } else { <p>@ Message</p> } @ code { [Parameter] public string? Message { get; set; } } Don't mind the spaces after the @!
|
-1

Your Razor content seems to be treated as Razor Pages or Razor Templates (MVC-style), but you're setting it up to compile as a Blazor component (ComponentBase).

The key problem is:

  • The generated class (Template) is trying to override ExecuteAsync, which exists in Razor Pages, but not in Blazor components.

  • Write and WriteLiteral are template methods used in Razor Pages (i.e., .cshtml), not in .razor files for Blazor.

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

string template = "Hello, @Model.Name";
var model = new { Name = "Gohulan" };
string result = await engine.CompileRenderAsync("templateKey", template, model);

1 Comment

This is not the answer to what I asked, you are 1) repeating what I said and 2) directing me to an external library, which is not what I asked for

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.