toddams / RazorLight

Template engine based on Microsoft's Razor parsing engine for .NET Core
Apache License 2.0
1.51k stars 260 forks source link

Compile string without model #337

Closed lwansbrough closed 3 years ago

lwansbrough commented 4 years ago

Is a method like Task<ITemplatePage> CompileStringAsync(string key, string source) possible?

jzabroski commented 4 years ago

Not sure what you're asking. - How to compile a template that has no model?

Just use a phantom model with type @Model System.Object

lwansbrough commented 4 years ago

That's basically what I did. Seems to work, just wasn't sure if that was a good idea or not. My question was about whether it was possible to compile without a model at all. For instance, it would be useful to be able to do something like this:

var templateSource = "@Model MyType\n<html/>";
var template = await CompileStringAsync("key", templateSource);
var rendered = await _razorEngine.RenderTemplateAsync(template, myTypeInstance, typeof(MyType));

This way I don't have to know the model type at compile time. This allows me to re-render the compiled template without having to recompile each time. (My use case is allowing dynamically created email templates to be batched to many users with templating in an efficient way.)

jzabroski commented 4 years ago

Interesting, thanks for sharing. I'll think about it. If you have a solution, please post it here for further discussion.

fhucko commented 4 years ago

I think that when you use CompileRenderStringAsync (shown in quick start), it will be compiled and rendered when run for first time, and in following calls it will use precompiled templates. I think it works like that, because why else would there be a caching provider?

If I am correct, you do not need to precompile the templates beforehand, it will be done in first render. If you really need the precompilation, you can compile-render each template with a model created just for precompilation.

jzabroski commented 4 years ago

@lwansbrough

This way I don't have to know the model type at compile time. This allows me to re-render the compiled template without having to recompile each time. (My use case is allowing dynamically created email templates to be batched to many users with templating in an efficient way.)

I think you might be conflating two requirements in your design.

  1. Dynamically selecting a model type at run-time. (RE: "I don't have to know the model type at compile time.")
  2. Dynamically creating templates in an efficient way

For one, this is a common feature request for ASP.NET Razor, but Microsoft hasn't addressed it. Basically, you can't create a template on an "open generic". For example, Razor doesn't support @Model IEnumerable<T>. Support for open generics would likely also be "templating in an efficient way", since it would internalize a lot of details to the JIT, which has very efficient support for compiling generically typed code.

For two, I have a very hard time believing its more efficient to generate a template the way you're describing. Even so, just use a phantom type and pass a singleton value to RazorLight. If you want to cache the output for a given template key, use a fast, in-memory caching framework like Microsoft Research's FASTER.

Ordinarily, the way Razor supports model-less Views is through ViewBags, but that approach is most likely NOT efficient since it uses ExpandoObjects, rather than a class property/function which is simply a pointer-to-member-function in a C++ v-table. I'd have to see more of your code to weigh in with more advice.