为什么RazorEngine动态地使用匿名类型模型编译模板?

Cha*_*ion 5 c# dynamic anonymous-types razor

我注意到RazorEngine.Compile()似乎对匿名类型的处理方式与其他类型不同.例如,请考虑以下代码:

public static void Main()
{
    try {
    var model = new { s = default(string) };
    RazorEngine.Razor.Compile("@Model.s.Length", model.GetType(), "a");
    RazorEngine.Razor.Run(model, "a");
    } catch (Exception ex) {
        Console.WriteLine(ex); // RuntimeBinderException (Cannot perform runtime binding on a null reference)
    }

    try 
    {
    var model = "";
    RazorEngine.Razor.Compile(@"@Model.Length", model.GetType(), "b");
    RazorEngine.Razor.Run(default(string), "b");
    } catch (Exception ex) {
        Console.WriteLine(ex); // NullReferenceException
    }

    try 
    {
    var model = Tuple.Create(default(string));
    RazorEngine.Razor.Compile(@"@Model.Item1.Length", model.GetType(), "c");
    RazorEngine.Razor.Run(model, "c");
    } catch (Exception ex) {
        Console.WriteLine(ex); // NullReferenceException
    }

        try 
    {
    var model = new Internal();
    RazorEngine.Razor.Compile(@"@Model.S.Length", model.GetType(), "d");
    RazorEngine.Razor.Run(model, "d");
    } catch (Exception ex) {
        Console.WriteLine(ex); // TemplateCompilationException (type Internal is not visible)
    }
}

internal class Internal {
    public string S { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我的理解是:匿名类型是内部的,所以通常Razor不会处理它们.但是,Razor通过生成动态模板为匿名类型提供特殊支持.

因此,我有两个问题:(1)我对这种行为的理解是否正确?(2)有没有办法让剃刀为匿名模型输出强类型模板?

Ben*_*ich 3

我有想法让 Razor 程序集成为您程序集的友元程序集(从而使内部可见),但这没有用。在Razor源码中,我们可以看到如下代码(在CompilerServiceBase.cs):

 if (modelType != null)
 {
     if (CompilerServices.IsAnonymousType(modelType))
     {
         type.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference(typeof(HasDynamicModelAttribute))));
     }
 }
Run Code Online (Sandbox Code Playgroud)

然后(在TemplateBaseOfT.cs):

HasDynamicModel = GetType().IsDefined(typeof(HasDynamicModelAttribute), true);
Run Code Online (Sandbox Code Playgroud)

稍后在同一文件中使用:

if (HasDynamicModel && !(value is DynamicObject) && !(value is ExpandoObject))
    model = new RazorDynamicObject { Model = value };
else
    model = value;
Run Code Online (Sandbox Code Playgroud)

所以是的 - 你的理解是正确的。此外,我们可以看到,使内部可见是不够的,因为 Razor 将任何匿名类型视为动态(RazorDynamicObject扩展DynamicObject)。如果包含程序集的内部可见,您可以尝试修补 Razor 代码,以不将匿名类型视为动态类型。

但在这种情况下,我认为代码必须发出一个新的公共类型,其中包含与模型的匿名类型相同的属性,以便 Razor 生成的代码可以实例化该类型的实例。或者,您可以使用此处描述的技巧来允许方法返回匿名类型的实例。