【问题标题】:Why does RazorEngine compile templates with anonymously-typed models dynamically?为什么 RazorEngine 会动态编译带有匿名类型模型的模板?
【发布时间】:2013-02-14 16:06:26
【问题描述】:

我注意到 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; }
}

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

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

【问题讨论】:

  • 在不了解 Razor 的工作原理的情况下,它看起来确实很有可能。虽然匿名类型确实是常规的“强”类型,但在编译模板时,实际上不可能肯定地引用使用的那个,即使他们想做这种巫术(因为可以从许多控制器调用模板。)
  • 虽然他们生成了一个类,但他们可以只为具有相同架构的本地匿名类型生成代码,并从一种类型映射到另一种类型。
  • 他们可以做各种各样的事情,但是当价值超过所需的努力时,他们会更容易下注。在这种情况下,除了为了强类型而对代码进行强类型之外,似乎没有什么好处。
  • 另外,为了让我的猜测不那么疯狂:new{a=""}.GetType().IsPublic 返回false。因此,似乎在定义它们的任何范围之外都无法访问匿名类型。

标签: c# dynamic razor anonymous-types


【解决方案1】:

我的想法是将 Razor 组件 a friend assembly 制作到您的组件中(从而使内部部件可见),但这没有奏效。在 Razor 源码中,我们可以看到如下代码(inCompilerServiceBase.cs):

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

然后(在TemplateBaseOfT.cs):

HasDynamicModel = GetType().IsDefined(typeof(HasDynamicModelAttribute), true);

稍后在同一个文件中使用:

if (HasDynamicModel && !(value is DynamicObject) && !(value is ExpandoObject))
    model = new RazorDynamicObject { Model = value };
else
    model = value;

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

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

【讨论】:

    【解决方案2】:

    Dynamic 用于允许匿名类型。当它编译模型时,它不能引用匿名类型,因为它们在您的程序集中。考虑到这一点,Razor 解析器正在尝试生成一个继承自 TemplateBase<T> 的模板,以提供强类型支持。这就是我们使用 TemplateBase<dynamic> 的原因,因为这意味着我们将对这些属性的访问权委托给 DLR,但您会失去静态类型类的好处。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-04
      • 1970-01-01
      • 2021-09-21
      • 1970-01-01
      • 1970-01-01
      • 2010-09-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多