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

With 2.0.0-beta1 how to use templates from memory without setting a project? #250

Closed
RedX2501 opened this issue Apr 6, 2019 · 24 comments
Closed

Comments

@RedX2501
Copy link
Contributor

RedX2501 commented Apr 6, 2019

Is your feature request related to a problem? Please describe.
Currently a ???Project must be passed to the builder to be able to render templates even when using CompileRenderStringAsync.

Describe the solution you'd like
It would be nice if nothing was needed if the templates I'm using are self-contained.

@RedX2501 RedX2501 changed the title With 2.0.0-beta1 how to use templates from memory? With 2.0.0-beta1 how to use templates from memory without setting a project? Apr 6, 2019
@jzabroski
Copy link
Collaborator

@RedX2501 What is a self-contained template? Do you mean passing in a Stream object that resolves to a cshtml file? What is the interface contract you want?

@mattwendels
Copy link

mattwendels commented Nov 22, 2019

@jzabroski
You used to be able to write:

var engine = new RazorLightEngineBuilder()
.UseMemoryCachingProvider()
.Build();

.. but this now throws an exception, saying the _razorLightProject cannot be null.

@jzabroski
Copy link
Collaborator

Which release was that behavior available in? I'm newer to RazorLight than others, but trying to help make it great.

I imagine the reason for the change is so that it more explicitly wires dependencies.

Did the old behavior just assume embedded resources, file system, or some other approach I'm not thinking of, like not using a key at all and instead passing in a whole cshtml file as a string?

@mattwendels
Copy link

I'm not sure to be honest. In the version I currently use (just trying to upgrade now) - we did:

var razorEngine = new RazorLightEngineBuilder().UseMemoryCachingProvider().Build();

... and then specified the entire template contents as a string:

var template = File.ReadAllText(razorTemplatePath);

var emailBody = razorEngine.CompileRenderAsync(razorTemplateCacheKey, template, viewModel).Result;

Looking at it, I think to get it working now I think instead of reading the file myself, I think I need to specifiy the directory containing the template via the .UseFileSystemProject(<template dir>) method and then just pass the template file name in to the .CompileRenderStringAsync() method...

I think?

@jzabroski
Copy link
Collaborator

Correct. I would recommend using embedded resources, though. The main reason to use file system is to allow hot swapping templates, but you lose control over your deployment artifacts when you do that. I don't trust engineers to not sneak on servers and change things. At companies I've been at, sometimes it's business analysts who are friends with the executives who get access and change things and then bring down a whole sub-section of your app.

tl;dr: Use EmbeddedResource Project instead of UseFileSystemProject

@mattwendels
Copy link

Thanks for the clarification. Much appreciated!

@mattwendels
Copy link

Sorry, just following up again here. I think there's a bit of design flaw here. In 2.0.0-beta1, you MUST specify a project type when creating the engine:

var razorEngine = new RazorLightEngineBuilder()
                .UseEmbeddedResourcesProject(typeof(EmailService)) // exception without this (or another project type)
                .UseMemoryCachingProvider()
                .Build();

But you then you can call razorEngine.CompileRenderStringAsync() and just pass a string in as your template, not using the resources provider at all.

I think just rendering a template from a string is a pretty common use-case, and seems confusing that you need to specify a project type (e.g. resources) that ultimately you'll never use.

Is there any scope to reintroduct the ability create a RazorEngine instance without a project type?

@jzabroski
Copy link
Collaborator

I mean, if you want to write test cases and the feature to support it, you are welcome to. But I suspect it's a lot of work to make sure there are no regressions, because everywhere you have a _razorProjectSystem you have a chance for a regression.

W.r.t using UseEmbeddedResourcesProject, the pattern Ivan uses in his tests is he creates a dummy class called Root that is in the Root namespace for your project. Not a great solution, but it's what I copied in my own projects.

@mattwendels
Copy link

Yeah, I was looking at the source earlier and I agree!

You're right though you can get around it by just specifying .UseEmbeddedResourcesProject(typeof(<current_class>) and then just use the CompileRenderStringAsync() without issue.

Hopefully this solves @RedX2501 original question.

Thanks again

@RedX2501
Copy link
Contributor Author

RedX2501 commented Nov 22, 2019

I'm quite glad this issue received so much attention :)

I'd really like to know WHAT a project is. Why I need it for and what are the implications of setting one? Is this in some documentation that I've missed?
I don't have an ASP.Net background and the documentation about Razor that I've found never talk about projects.

allow hot swapping templates, but you lose control over your deployment artifacts when you do that

This is exactly my use-case. I have a model which the users can access with their own templates to generate C++ code for their application.

By self-contained I mean that the template has no imports to additional files.

Hello @user. I'm a self contained template because I don't require anything else.

@mattwendels
Copy link

As far as I can tell a project is effectively the ‘source’ of your .cshtml templates. E.g a folder somewhere or an embedded resource etc.

But my point above is that we have a method on the RazorEngine that just lets you compile a template by passing it in as a string (CompileRenderStringAsync). The ‘project’ (or source) is not relevant, so why do we need to set it when creating the RazorEngine?

Looking at the source the project is used everywhere and it seems to have become enforced during a big redesign of the compilation process. I suspect this particular use-case was maybe forgotten about?

Anyway, as far as the implications go, I just set RazorEngine to use the embedded resource project, passing in the type of the current class, but carried on calling CompileRenderStringAsync() anyway and passing in a string as the template and everything seems to work fine.

The resource project is totally ignored as far as I can tell and I haven’t seen any issues (yet).

@jzabroski
Copy link
Collaborator

@RedX2501 Interesting, it would be cool to create a list in the README of projects using RazorLight and their use cases. I got involved because I want my project FluentMigrator to use it for customizing SQL templates. FluentMigrator is arguably the most powerful SQL abstraction tool out there for modifying db schemas. But a pain point exists around SQL-level overrides ; core templates cannot be trivially overridden.

@RedX2501
Copy link
Contributor Author

RedX2501 commented Nov 22, 2019

Sorry, just following up again here. I think there's a bit of design flaw here. In 2.0.0-beta1, you MUST specify a project type when creating the engine:

var razorEngine = new RazorLightEngineBuilder()
                .UseEmbeddedResourcesProject(typeof(EmailService)) // exception without this (or another project type)
                .UseMemoryCachingProvider()
                .Build();

Excuse my stupidity but I just compiled 2.0.0-beta1 and I can't seem to find the RazorLightEngineBuilder class. Where is it?

I can't seem to find it anywhere with the available source code for beta 1:

image

@RedX2501
Copy link
Contributor Author

I'm absolutely confused now :( I installed version 2.0.0-beta1 from the nuget and voila. A RazorLightEngineBuilder is available. Yet in the source I can't find any trace of it...

Well if I use version 2.0.0-beta1 I get the following exception:

image

@jzabroski
Copy link
Collaborator

You need a German Delta Layer on RazorLight to prevent it from leaking 😆 https://www.youtube.com/watch?v=ZbQmKsXIBYc

@jzabroski
Copy link
Collaborator

100 jahre langzeitbestandigkeit

@RedX2501
Copy link
Contributor Author

Master (60fdee0) seems to work at least.

@RedX2501
Copy link
Contributor Author

I think we can close this as it seems to work now.

Maybe it would be interesting to retag 2.0.0-beta1 to the proper commit that was used to generate the nuget package...

@vijkumarsharma
Copy link

@jzabroski @mattwendels Can you please suggest to me what is the issue here

    public async Task<string> GenerateHtml(EmailModel model)
    {
        var engine = new RazorLightEngineBuilder()
       .UseFileSystemProject("C:/Users/vijku/source/repos/CalendarApp/CalendarApp/bin/Release/net6.0/win-x64/publish/win-x64")
       .UseMemoryCachingProvider()
       .Build();
        string result = await engine.CompileRenderAsync("Templates/Email.cshtml", model);

        return result;

    }
    
    
    I am getting the below error :

image

@jzabroski
Copy link
Collaborator

DefaultMetadataReferenceManager uses specific paths that may not be supported by your deployment option. This can happen if you compile everything into a giant dll where the CodeBase property is null. You'll have to implement an alternative if you stick with whatever deployment you are using that is causing this

@vijkumarsharma
Copy link

@jzabroski thanks for the help . I was able to fix the issue . Now i am getting below issue

Code used:

public async Task GenerateHtml(EmailModel model)
{
var engine = new RazorLightEngineBuilder()
.UseEmbeddedResourcesProject(typeof(EmailModel))
.EnableDebugMode(true)
.UseMemoryCachingProvider()
.Build();
string result = await engine.CompileRenderAsync("Email", model);

        return result;
        
        
Error coming
RazorLight.TemplateNotFoundException: RazorLightProjectItem of type RazorLight.Razor.EmbeddedRazorProjectItem with key Email.cshtml could not be found by the RazorLightProject of type RazorLight.Razor.EmbeddedRazorProject and does not exist in dynamic templates. See the "KnownDynamicTemplateKeys" and "KnownProjectTemplateKeys" properties for known template keys.
     at RazorLight.Compilation.RazorTemplateCompiler.CreateRuntimeCompilationWorkItem(String templateKey)
     at RazorLight.Compilation.RazorTemplateCompiler.OnCacheMissAsync(String templateKey)
     at RazorLight.EngineHandler.CompileTemplateAsync(String key)
     at RazorLight.EngineHandler.CompileRenderAsync[T](String key, T model, ExpandoObject viewBag)
     at CalendarAppService.EmailSender.GenerateHtml(EmailModel model) in C:\Users\vijku\source\repos\CalendarApp\CalendarApp\CalendarApp.cs:line 179
     at CalendarAppService.EmailSender.Main() in C:\Users\vijku\source\repos\CalendarApp\CalendarApp\CalendarApp.cs:line 47
     at CalendarApp.Worker.ExecuteAsync(CancellationToken stoppingToken) in C:\Users\vijku\source\repos\CalendarApp\CalendarApp\Worker.cs:line 21
     at Microsoft.Extensions.Hosting.Internal.Host.TryExecuteBackgroundServiceAsync(BackgroundService backgroundService)

@jzabroski
Copy link
Collaborator

Please re-read the error message for next steps:

RazorLight.TemplateNotFoundException: RazorLightProjectItem of type RazorLight.Razor.EmbeddedRazorProjectItem with key Email.cshtml could not be found by the RazorLightProject of type RazorLight.Razor.EmbeddedRazorProject and does not exist in dynamic templates. See the "KnownDynamicTemplateKeys" and "KnownProjectTemplateKeys" properties for known template keys.

@vijkumarsharma
Copy link

vijkumarsharma commented Sep 20, 2022

@jzabroski
yeah, I did get through an error, but no fruitful result as such. do you know how to fix it?
I tried many things but no result.

saw this link as well #474

@jzabroski
Copy link
Collaborator

What is not fruitful?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants