RazorEngineCore
RazorEngineCore copied to clipboard
how can I reference other assemblies and use inject?
I tried to use localization in razor:
@using Microsoft.Extensions.Localization
@inject IStringLocalizer Localizer
@{
<h1>Localizer["Header"]<h1>;
}
I added reference to my project. Result: Unable to compile template: skvbsxxk.c3i(3,17): error CS0234: The type or namespace name 'Extensions' does not exist in the namespace 'Microsoft'
When I add any other reference I get the same result. Can I manage this?
Also having issues making use of @using directives.
If you need to inject something in text template or reference other assemblies, probably you are overengeneering. Its not a RazorEngine responsibility go get service instances for you.
Making Inject directive work would require IoC container to be built in. I would like to avoid that and keep engine as simple as possible.
Localizer
Intended use
public class MyTemplateBase : RazorEngineTemplateBase
{
private IStringLocalizer localizer;
public void Initialize(IStringLocalizer localizer)
{
this.localizer = localizer;
}
public string Localize(string key)
{
return this.localizer[key];
}
}
string templateText = @"
<h1>@Localize(""Header"")</h1>
";
RazorEngine razorEngine = new RazorEngine();
RazorEngineCompiledTemplate<MyTemplateBase> compiledTemplate = razorEngine.Compile<MyTemplateBase>(templateText);
string result = compiledTemplate.Run(instance =>
{
instance.Initialize(this.localizerInstance); // get instance from whatever source
});
Referencing assemblies
Usings in order to work require assemblies to be referenced on compilation phase
RazorEngine razorEngine = new RazorEngine();
RazorEngineCompiledTemplate compiledTemplate = razorEngine.Compile(templateText, builder =>
{
builder.AddAssemblyReferenceByName("System.Security"); // by name
builder.AddAssemblyReference(typeof(System.IO.File)); // by type
builder.AddAssemblyReference(Assembly.Load("source")); // by reference
});
string result = compiledTemplate.Run(new { name = "Hello" });
Maybe it is possible to use default DI for .net core? i.e https://github.com/toddams/RazorLight/blob/master/src/RazorLight/Extensions/ServiceCollectionExtensions.cs
Can you tell what exactly are you building so I can understand better?
- I use standard DI container in my Startup class.
- I have implementation of interface IStringLocalizer with registration in DI.
I would like to have possibility to to register engine in DI and use inject IStringLocalizer in my razor template without RazorEngineTemplateBase
+ for adding injection. Here is my use case. I'm serving fragments to my pages using SSE and a JIT approach. A page has many "Views" (containers for HTML Content) that get populated on-demand based on current conditions. A View has HTML, JavaScript Modules, and CSS that get registered in the page when received. I have a set of builder services that generate parts of what I am sending.
// ViewComponent1.cshtml
@inject HTMLBuilder _html
@inject ESMBuilder _script
<viewPart>
[static content]
@await _html.generateViewContent(conditions)
[static content]
</viewPart>
<script type="module">
@await _script.generateViewController(controller)
@await _script.generateViewModel(modelClassDef)
@await _script.generateViewModel(modelClassDef)
</script>
// C# on Server
... receive request
// business processes
var controller = // determine controller
var models = new[] { // determine models }
var viewComponent = renderJitView(controller, models);
// Send with SSE
await responseStream.WriteAsync("data: { component: viewComponent } \n\n");
await responseStream.Body.FlushAsync();
With inject I could do deeper composition. (Made up RazorEngineCore syntax)
<layoutContainer>
<viewPartContainer locator="/1/">
@await RazorEngineCore.renderViewWithInjects(ViewComponent1, params1)
</viewPartContainer>
<viewPartContainer locator="/2/">
@await RazorEngineCore.renderViewWithInjectss(ViewComponent2, params2)
</viewPartContainer>
</layoutContainer>
What I am sending is not an empty or static template. The fields are generated dynamically. It is a fully populated view with a custom "ViewController" and multiple "custom" models that can be sent back to the server. Inject is what I use to compose these components in real time.
@Randy-Buchholz How many builder services you have? My first thought is to make them avaiable by adding in TemplateBase class.
There is no set number of builders. Each business domain provides a set of builders. For example, the Shipping domain might have builders for Ground Shipping and Air Shipping. If someone selects "Ground" a fragment is built and sent to the browser to populate a "Shipping" section of the page. "Ground" may have a builder for Carriers that would build fragments based on individual shippers.
What I am building is a framework to manage the creation and delivery of the fragments. I can render single-level Razor without problems and then put them together. But that gets messy.
This may not make a lot of sense without knowing more, but conceptually I'm doing something like this:
@inject Shipping _shipping
@inject Ground _delivery
@inject DHL _carrier
<container>
@render(_shipping)
<container>
// Injected into container
<shipping>
@render(_delivery)
</shipping>
// Injected into shipping
<delivery>
@render(_carrier)
<delivery>
// Injected into delivery
<carrier>
</carrier>
Right now I render each one individually and merge them
Your point is clear.
Well primary issue for me is that @inject
is not default Razor keyword, however all @entries
got parsed and stored somewhere, I dont know how to access this information.
Generally speaking I do not want to tie RazorEngineCore to particular IoC container unless there is no other choice.
If we were able to locate and process injects
collection, it would be easy to come up with sutable solution