Introducing RazorSharpener

Introduction

Sharpener: a person or device that makes something sharp (Cambridge Dictionary).

RazorSharpener is a Razor compiler and renderer. It can load a .razor file and compile it into a .NET class that implements IComponent, the base interface for Razor components.

This can be used, for example, to build a template engine, as Razor allows us to combine contents and code.

Usage

RazorSharpener is composed of just two classes:

Razor Compiler

To compile a .razor file is very simple:

var compiler = new RazorCompiler();
var asm = compiler.Compile("RenderMessage.razor");

As you can see, the Compile method takes a file from the current folder and produces an assembly, which is how .NET "wraps" generated code. If there is any compilation error, an exception is thrown. The generated assembly will have a single .NET type that implements IComponent.

There are overloads for passing arbitrary .NET assemblies, that will be used for linked references, and also compiler options. The defaults should be enough, at least, for simple cases and I won't cover more complex scenarios here.

Razor Renderer

The renderer is only slightly more complex, but the class itself is called RazorRenderer:

var renderer = new RazorRenderer();

Now, there are two overloads of its Render method:

1) One that takes a .razor class:

var html = await renderer.Render<RenderMessage>();

Note: the .csproj must have this .razor file configured as Content and Copy if newer so that it is available on the output folder:

2) And another that takes a Type, which must implement IComponent, and you can get it from the assembly generated by RazorCompiler:
var componentType = asm.GetTypes().First();
var html = await renderer.Render(componentType);

As you can see, both overloads are asynchronous and have to be awaited. Also, both have an overload that takes a parameter dictionary:

var parameters = new Dictionary<string, object?>
{
    ["Message"] = "Hello, world!"
};
var html = await renderer.Render<RenderMessage>(parameters);
//or
var html = await renderer.Render(componentType, parameters);

Parameters must match any parameters defined on the .razor file, for example, here is my sample RenderMessage.razor:

@if (string.IsNullOrWhiteSpace(Message))
{
    <p>No message provided</p>
}
else
{
    <p>@Message</p>
}
@code {
    [Parameter]
    public string? Message { get; set; }
}

Of course, any parameters passed to Render must match the declared parameter type - the property to which [Parameter] is applied.

And this is it! Render will render the component as HTML (or whatever it produces) and return the generated output.

Conclusion

I hope you find this useful, as always, looking forward for your comments or questions. You can expect some more features and improvements in the future.

GitHub: https://github.com/rjperes/RazorSharpener

Nuget: https://www.nuget.org/packages/RazorSharpener

Comments

  1. The need to target .Net Web Sdk is kind of restrictive, since an interesting use case for this library would be on application layers dispatching templated emails, for instance.

    ReplyDelete
    Replies
    1. Right, let me explain that: in order to automatically compile the RenderMessage.razor file into a RenderMessage .NET class you need this, but that is all. You can use the library without it. Thanks for your comment!

      Delete
    2. Actually, it seems that it is not needed, I was wrong! :-)

      Delete

Post a Comment

Popular posts from this blog

OpenTelemetry with ASP.NET Core

ASP.NET Core Middleware

.NET Cancellation Tokens