Search code examples
c#razorrazorengine

TemplateService.Parse gives "Invalid token for impersonation - it cannot be duplicated" when using anonymous model and cache name


I have followed this tutorial to use the RazorEngine to generate HTML emails (without ASP.NET MVC). It worked fine when I used either a strongly typed view or an anonymous object as the model:

Layout.cshtml

@model dynamic
// html contents...

C# snippet

var template = Encoding.UTF8.GetString(Properties.Resources.ContractList);

var model = new
{
    ContractList = list
};

var templateService = new TemplateService();
var html = templateService.Parse(template, model, null, null);

Then I gave it a cache name, as recommended on the third part of the tutorial:

templateService.Parse(template, model, null, nameof(Properties.Resources.ContractList));

It worked fine with the strongly typed object, but with the anonymous object I started receiving this message as of the second time it ran:

Invalid token for impersonation - it cannot be duplicated

How can I resolve this and still use an anonymous object for the model?

I have seen solutions (here, here) that suggest transforming the anonymous object to an ExpandoObject, but I'd like to avoid that if possible.


Solution

  • After reading the RazorEngine docs, I found some examples on their upgrading page which showed how to use Engine.Razor. The easiest way to do it is:

    var html = Engine.Razor.RunCompile(
        template,
        nameof(Properties.Resources.ContractList),
        null,
        model
    );
    

    This solved the issue. Just beware that they don't recommend the above format, because internally it will call AddTemplate every time, and that it will throw an exception when you use the same cache_name for different templates. Instead they recommend precompiling on startup:

    // Once at startup (not required when using an ITemplateManager which knows how to
    // resolve cache_name)
    Engine.Razor.AddTemplate(cache_name, razorTemplate)
    // On startup
    Engine.Razor.Compile(
        cache_name, 
        typeof(MyModel) /* or model.GetType() or null for 'dynamic'*/
    )
    
    // instead of the Razor.Parse call
    var result = Engine.Razor.Run(
        cache_name,
        typeof(MyModel), /* or model.GetType() or null for 'dynamic'*/
        model
    )