Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@Model is null when using RazorEngineTemplateBase #28

Open
kobynz opened this issue Aug 5, 2020 · 8 comments
Open

@Model is null when using RazorEngineTemplateBase #28

kobynz opened this issue Aug 5, 2020 · 8 comments
Labels
enhancement New feature or request

Comments

@kobynz
Copy link

kobynz commented Aug 5, 2020

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.

@kobynz
Copy link
Author

kobynz commented Aug 5, 2020

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

@adoconnection
Copy link
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.

@adoconnection
Copy link
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;
    });
}

@farlee2121
Copy link

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

@farlee2121
Copy link

farlee2121 commented Sep 29, 2020

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

@adoconnection
Copy link
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; }
}

@farlee2121
Copy link

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.

@adoconnection
Copy link
Owner

good news anyway :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants