Antaris / RazorEngine

Open source templating engine based on Microsoft's Razor parsing engine
http://antaris.github.io/RazorEngine
Other
2.14k stars 577 forks source link

How to wrap up razor for use in my own business logic #422

Open TehWardy opened 7 years ago

TehWardy commented 7 years ago

I can get the razor engine to work when I use only the default static instance of it but as soon as I start wrapping it up and working on a "custom instance" provided by my IoC container things start to go pear shaped ...

IoC rules ...

Rebind<IReferenceResolver>().To<WorkflowTransformReferenceResolver>();
Bind<IEncodedStringFactory>().To<RawStringFactory>();
Bind<ITemplateServiceConfiguration>().ToMethod(ctx => {
    return new TemplateServiceConfiguration
    {
        ReferenceResolver = ctx.Kernel.Get<IReferenceResolver>(),
        EncodedStringFactory = ctx.Kernel.Get<IEncodedStringFactory>(),
        Language = RazorEngine.Language.CSharp
    };
});
Bind<IRazorEngineService>()
    .ToMethod(ctx => RazorEngineService.Create(ctx.Kernel.Get<ITemplateServiceConfiguration>()))
    .InSingletonScope();

Wrapper ...

public class Razor
{
    IRazorEngineService razor;

    public Razor(IRazorEngineService razor)
    {
        this.razor = razor;
    }

    public void Cache<T>(string key, string templateString)
    {
        if (razor.IsTemplateCached(key, typeof(T)))
            razor.RemoveTemplate(key);                // doesn't exist ... how do I remove a template?

        razor.AddTemplate(key, new LoadedTemplateSource(templateString));
        razor.Compile(razor.GetKey(key), typeof(T));  // Raises exception below
        /*
        An exception of type 'System.NotSupportedException' occurred in mscorlib.dll but was not handled in user code
        Additional information: The invoked member is not supported in a dynamic assembly.

        Stack ...
    at System.Reflection.Emit.InternalAssemblyBuilder.get_Location()
   at RazorEngine.Compilation.ReferenceResolver.CompilerReference.GetHashCode()
   at System.Collections.Generic.ObjectEqualityComparer`1.GetHashCode(T obj)
   at System.Collections.Generic.HashSet`1.InternalGetHashCode(T item)
   at System.Collections.Generic.HashSet`1.AddIfNotPresent(T value)
   at RazorEngine.Templating.ReferencesListForDynamicAssemblyResolution.AddReferences(IEnumerable`1 refs)
   at RazorEngine.Compilation.TypeContext.AddReferences(IEnumerable`1 references)
   at RazorEngine.Compilation.CompilerServiceBase.GetAllReferences(TypeContext context)
   at RazorEngine.Compilation.DirectCompilerServiceBase.Compile(TypeContext context)
   at RazorEngine.Compilation.DirectCompilerServiceBase.CompileTypeImpl(TypeContext context)
   at RazorEngine.Compilation.DirectCompilerServiceBase.CompileType_Windows(TypeContext context)
   at RazorEngine.Compilation.DirectCompilerServiceBase.CompileType(TypeContext context)
   at RazorEngine.Templating.RazorEngineCore.CreateTemplateType(ITemplateSource razorTemplate, Type modelType)
        */
    }

    public string Render<T>(string key, T model)
    {
        return razor.Run(key, typeof(T), model);
    }
}

My custom reference resolvier appears to be the problem with the exception being raised ...

public class WorkflowTransformReferenceResolver : IReferenceResolver
{
    public IEnumerable<CompilerReference> GetReferences(TypeContext context, IEnumerable<CompilerReference> includeAssemblies)
    {
        // get the collective set of assembly refs we care about
        var results = new UseCurrentAssembliesReferenceResolver()
            .GetReferences(context, includeAssemblies)
            .ToList();

        var refStrings = results.Select(r => r.GetFile());

        var stackRefs = TypeHelper.GetWebStackAssemblies()
            .Select(a => CompilerReference.From(a))
            .Where(r => !refStrings.Any(s => {
                try { return s == r.GetFile(); }
                catch { return false; }
            }))
            .ToList();

        results.AddRange(stackRefs);

        // return them
        return results;
    }
}
TehWardy commented 7 years ago

What makes an assembly usable here ... kind of hard to tell?

matthid commented 7 years ago

I think this is a problem with your IReferenceResolver implementation. You need to check if the assembly is a dynamic assembly before accessing the Location property.

See this filter we have here: https://github.com/Antaris/RazorEngine/blob/master/src/source/RazorEngine.Core/Compilation/ReferenceResolver/UseCurrentAssembliesReferenceResolver.cs#L25

TehWardy commented 7 years ago

None of the assemblies returned by my type helper are dynamic it returns a core set of referenced assemblies ... I have since solved the problem by telling my type helper to not only resolve them but also ensure that they are loaded.

As the type helper call is made during app load it means I could remove the reference resolver here and allow it to fall back to the default one.

In short ... I just use what's in the app domain.

But it still won't use extension methods for some reason.

matthid commented 7 years ago

So this can be closed?

TehWardy commented 7 years ago

Yeh I guess but the rules around what can and can't be used in a custom reference resolver probably need better docs.

I have a workable solution to my issue though by basically having my helper force the right stuff to be in the loaded stuff in the app domain then calling the default one