Skip to content

@Model is null when using RazorEngineTemplateBase #28

Open

Description

@kobynz

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.

Activity

  1. kobynz commented on Aug 5, 2020

    @kobynz
    Author

    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>.

  2. adoconnection commented on Aug 6, 2020

    @adoconnection
    Owner

    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.

  3. adoconnection commented on Aug 6, 2020

    @adoconnection
    Owner

    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;
        });
    }
    
  4. farlee2121 commented on Sep 29, 2020

    @farlee2121

    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
    dotnet/csharplang#2844

  5. farlee2121 commented on Sep 29, 2020

    @farlee2121

    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

  6. adoconnection commented on Sep 29, 2020

    @adoconnection
    Owner

    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; }
    }
  7. farlee2121 commented on Sep 29, 2020

    @farlee2121

    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.

  8. adoconnection commented on Sep 30, 2020

    @adoconnection
    Owner

    good news anyway :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions