@Model is null when using RazorEngineTemplateBase
I've run into an issue when attempting to strongly type a template.
My template has @inherits RazorEngineCore.RazorEngineTemplateBase<MyModelType> at the top, and this makes intellisense work nicely.
The template compiles just fine, but when I run it, I always get "Object not sent to instance of an object" when I reference any property on my model. I know my model is not null. It works fine when render it without using RazorEngineTemplateBase.
Running a template with @(Model == null) works, but always renders "True".
My generic render function:
private static ConcurrentDictionary<int, IRazorEngineCompiledTemplate<IRazorEngineTemplate>> TemplateCache =
new ConcurrentDictionary<int, IRazorEngineCompiledTemplate<IRazorEngineTemplate>>();
public static string Render<TModel>(string template, TModel model, Assembly[] referencedAssemblies = null)
{
int templateHashCode = template.GetHashCode();
var compiledTemplate = TemplateCache.GetOrAdd(templateHashCode, i =>
{
var razorEngine = new RazorEngine();
var compiledTemplate = razorEngine.Compile<RazorEngineTemplateBase<TModel>>(template, builder =>
{
...
});
return compiledTemplate;
});
return compiledTemplate.Run(instance =>
{
instance.Model = model;
});
}
Any help on this would be greatly appreciated. Thanks.
Another update on this, It seems my "weakly typed" version of this function is able to render my template just fine.
My template still has @inherits RazorEngineCore.RazorEngineTemplateBase<MyModelType>
private static ConcurrentDictionary<int, IRazorEngineCompiledTemplate> TemplateCache =
new ConcurrentDictionary<int, IRazorEngineCompiledTemplate>();
public static string Render(string template, object model, Assembly[] referencedAssemblies = null)
{
int templateHashCode = template.GetHashCode();
var compiledTemplate = TemplateCache.GetOrAdd(templateHashCode, i =>
{
var razorEngine = new RazorEngine();
return razorEngine.Compile(template, builder =>
{
if (referencedAssemblies != null)
{
foreach (var assembly in referencedAssemblies)
{
builder.AddAssemblyReference(assembly);
}
}
});
});
return compiledTemplate.Run(model);
}
I'm guessing the null issue is related to using the generic Compile<T>.
Its because of new keywork I used: https://github.com/adoconnection/RazorEngineCore/blob/master/RazorEngineCore/RazorEngineTemplateBaseT.cs
Thats design issue, I need to think a little.
as a quick solution I would simplify cache dictionary:
private static ConcurrentDictionary<int, object> TemplateCache = new ConcurrentDictionary<int, object>();
public static string Render<TModel>(string template, TModel model, Assembly[] referencedAssemblies = null)
{
int templateHashCode = template.GetHashCode();
var compiledTemplate = (IRazorEngineCompiledTemplate<RazorEngineTemplateBase<TModel>>) TemplateCache.GetOrAdd(templateHashCode, i =>
{
var razorEngine = new RazorEngine();
var compiledTemplate = razorEngine.Compile<RazorEngineTemplateBase<TModel>>(template, builder =>
{
});
return compiledTemplate;
});
return compiledTemplate.Run(instance =>
{
instance.Model = model;
});
}
This is a design expression issue I've long struggled with in C#.
C# 9 will supposedly be implementing covariant overrides. That would solve this issue, but is a ways out https://github.com/dotnet/csharplang/issues/2844
Maybe not so far out. Looks like they plan to release C# 9 with .NET 5, which is already to release candidates https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version
Cool, however this breaking change for new keywork is confusing.
I would prefer something like this, with override keyword
public abstract class RazorEngineTemplateBase<T> : RazorEngineTemplateBase
{
public override T Model { get; set; }
}
public abstract class RazorEngineTemplateBase : IRazorEngineTemplate
{
public virtual dynamic Model { get; set; }
}
That appears to be the design they're going for. https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/covariant-returns.md#motivation
I wasn't clear that this library would need to change from new to override.
good news anyway :)