0

Problem

I am migrating our main project to Net 5. Its main feature is to generate documents using RazorEngine. After migrating everything to Net5, the application is running as intended except for the RazorEngine. After doing quite a bit of testing I have found that RazorEngine.NetCore is missing some functionality or has been refactored to function differently.

The first issue is that the RazorEngine.NetCore does not support all the syntax as its predecessor, such as the @functions. I found a forked version on github that is a bit more effort to get implemented but does work. However, management would prefer this to be a nuget package that we do not have to manage.

The second issue, which I have yet to find a work around, is that we need to inject logic into every template to create variables (who's values are determined during runtime). For example, we determine a directory location and create a string version of a code block for a cshtml document. That string is concatenated to the contents of a template before being processed by RazorEngine.Engine.Razor.RunCompile(...). The RazorEngine throws an exception because the template tries to use one of the variables injected but it does not exist. If I inspect the fully concatenated string of the injected code block with the templates content and then manually add that same code block directly into the template, it does compile and functions as intend. I manually added the code into the template for testing, but this would not work for production, and thus here we are.

The code below is one code block being injected into the templates. If this same line is added as the first line of the template, it works. What I don't understand is that the string being provided to RazorEngine.Engine.Razor.RunCompile(...) is exactly the same (whether the code below is concatenated with the contents of the template or the string comes entirely from the template).

Scenario 1 - Concatenated

report.cshtml: ...the template's contents...

string templateReference = @"C:\templates\report.cshtml" // hard-coded here for simplicity
TemplateKey templateKey = (TemplateKey)Engine.Razor.GetKey(templateReference);
string path = @"C:\some\other\directory\";
string header = $"@{{const string templatePath = @\"{path}\";}}";
string templateContent = // contents of the report.cshtml file
string preProcessedTemplate = header + templateContent;
string output = Engine.Razor.RunCompile(preProcessedTemplate, templateKey, null, model, viewBag);

Scenario 2 - Embedded

report.cshtml: @{const string templatePath = @"C:\some\other\directory\";}...the template's contents...

string templateReference = @"C:\templates\report.cshtml" // hard-coded here for simplicity
TemplateKey templateKey = (TemplateKey)Engine.Razor.GetKey(templateReference);
string preProcessedTemplate = // contents of the report.cshtml file
string output = Engine.Razor.RunCompile(preProcessedTemplate, templateKey, null, model, viewBag);

Results

string preProcessedTemplate = "@{const string templatePath = @\"C:\\some\\other\\directory\\\";}...the template's contents..."
Scenario 1: Throws Exception
Scenario 2: Returns compiled template

Questions

  1. RazorEngine.NetCore is not an official Microsoft RazorEngine. Is there plans for an official version?
  2. Why does RazorEngine.Engine.Razor.RunCompile(...) only compile scenario 2 if the strings from both scenarios are identical?
  3. Is there an alternative to RazorEngine for Net 5 that would not require refactoring the cshtml templates?

1 Answers1

0
  1. RazorEngine.NetCore is not an official Microsoft RazorEngine. Is there plans for an official version?

If "official" means produced by Microsoft, doubt this will happen soon.

  1. Why does RazorEngine.Engine.Razor.RunCompile(...) only compile scenario 2 if the strings from both scenarios are identical?

Please let us know the exact Exception?

  1. Is there an alternative to RazorEngine for Net 5 that would not require refactoring the cshtml templates?

Try RazorLight, it's pretty cool and works well. I've tried out a few "Razor Engines", as you pointed out, many are retired or not supporting latest official Razor updates. I found RazorLight to be the best all things considered. Of course RazorEngine is a newer library and built without legacy, though I am just suggesting a quick fix alternative option.

Some example code for RazorLight

var engine = new RazorLightEngineBuilder()
    .UseFileSystemProject("C:/RootFolder/With/YourTemplates")
    .UseMemoryCachingProvider()
    .Build();

var model = new {Name = "John Doe"};
string result = await engine.CompileRenderAsync("Subfolder/View.cshtml", model);

Issue: The second issue, which I have yet to find a work around, is that we need to inject logic into every template to create variables (who's values are determined during runtime).

Suggest you move this logic into the model being passed into the view, rather than altering the cshtml. Obvious SOLID principle benefits and far easier to test the models in isolation. This is what our team does when using RazorLight for generating documents.

Rax
  • 665
  • 8
  • 19