Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how can I reference other assemblies and use inject? #6

Open
yamaritta opened this issue Mar 4, 2020 · 10 comments
Open

how can I reference other assemblies and use inject? #6

yamaritta opened this issue Mar 4, 2020 · 10 comments
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@yamaritta
Copy link

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?

@roja
Copy link
Contributor

roja commented Mar 4, 2020

Also having issues making use of @using directives.

@adoconnection
Copy link
Owner

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
});

@adoconnection
Copy link
Owner

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" });

@yamaritta
Copy link
Author

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

@adoconnection
Copy link
Owner

Can you tell what exactly are you building so I can understand better?

@yamaritta
Copy link
Author

  1. I use standard DI container in my Startup class.
  2. 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

@adoconnection adoconnection added the enhancement New feature or request label Jul 1, 2020
@Randy-Buchholz
Copy link

Randy-Buchholz commented Sep 11, 2020

+ 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.

@adoconnection
Copy link
Owner

adoconnection commented Sep 15, 2020

@Randy-Buchholz How many builder services you have? My first thought is to make them avaiable by adding in TemplateBase class.

@Randy-Buchholz
Copy link

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

@adoconnection
Copy link
Owner

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

@adoconnection adoconnection added the help wanted Extra attention is needed label Sep 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

4 participants