在EditorFor中用于子对象时,MVC无法覆盖EditorTemplate名称

jim*_*ate 10 asp.net-mvc mvc-editor-templates

我正在尝试使用EditorTemplate在父视图的表中显示子集合.我遇到的问题是,如果模板的名称与子类的名称完全相同,这似乎只能起作用.当我尝试使用名称略有不同的模板,并将该名称作为templateName参数传递给EditorFor时,我收到运行时错误.我希望我可以使用相同的子集合将不同的子EditorTemplates用于不同的目的.这是一个简短的例子:

楷模:

public class Customer
{
  int id { get; set; }
  public string name { get; set; }

  public List<Order> Orders { get; set; }
}
public class Order
{
    public int id { get; set; }
    public DateTime orderdate { get; set; }
    public decimal amount { get; set; }

    public Customer customer { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

客户控制器Index()方法:

public ActionResult Index()
{
  Customer customer = new Customer() {id = 1, name = "Acme Corp.", Orders = new List<Order>()};
  customer.Orders.Add(new Order() {id = 1, orderdate = DateTime.Now, amount = 100M});
  customer.Orders.Add(new Order() { id = 2, orderdate = DateTime.Now, amount = 200M });
  return View(customer);
}
Run Code Online (Sandbox Code Playgroud)

客户Index.cshtml视图:

@model TemplateTest.Customer

@{
  Layout = null;
}

<!DOCTYPE html>

<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Customer</title>
</head>
<body>
  <div>
      @Html.EditorFor(Model=>Model.name)

      <table>
      <thead>
          <tr>
              <th>Order ID</th>
              <th>Order Date</th>
              <th>Amount</th>
          </tr>
      </thead>
          @Html.EditorFor(Model=>Model.Orders)
      </table>

  </div>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

Views/Shared/EditorTemplates中的Order.cshmtl模板(添加"color"以验证我使用此模板):

@model TemplateTest.Order

<tr>
  <td>@Html.DisplayFor(Model=>Model.id)</td>
  <td style="color:blue">@Html.EditorFor(Model=>Model.orderdate)</td>
  <td>@Html.EditorFor(Model=>Model.amount)</td>
</tr>
Run Code Online (Sandbox Code Playgroud)

这很好用.但是,如果我将EditorTemplate重命名为"OrderList.cshtml"并将子EditorFor行更改为

@Html.EditorFor(Model=>Model.Orders, "OrderList")
Run Code Online (Sandbox Code Playgroud)

当我再次运行它时,我得到这个例外:

"传递到字典中的模型项的类型为'System.Collections.Generic.List`1 [TemplateTest.Order]',但此字典需要一个类型为'TemplateTest.Order'的模型项."

知道为什么EditorFor不使用我在"templateName"参数中指定的模板"OrderList"吗?否则,该参数是什么?

Dav*_*sey 24

TL; DR>命名模板不适用于集合,使用foreach循环来解决它 - 请参阅下面有关原因的详细信息和示例.


你说:

知道为什么EditorFor不使用我在"templateName"参数中指定的模板"OrderList"吗?否则,该参数是什么?

EditorFor实际上是使用OrderList你指定的模板- 但你偶然发现了一些令人困惑的事情.一些研究发现了很多提示,但我在这篇文章中找到了真正的细节:MVC EditorFor命名模板的问题

简而言之,正在发生的事情是,默认情况有效:@Html.EditorFor(Model=>Model.Orders)实际上是按照惯例在临时调用MVC默认模板,但这根本不明显.

试着这样想:

在工作版本中,您传入一个List<Order>引用Model.Orders(MANY orders)的类型,但模板指定的模型为Order(single,NOT MANY).

有趣.为什么这甚至有效?乍看之下,似乎应该工作.但它确实有效,因为幕后发生了什么.

从上述帖子转述:

当您使用@Html.EditorFor(c => c.Orders)MVC约定时,选择 默认模板IEnumerable.此模板是MVC框架的一部分,它Html.EditorFor()为枚举中的每个项生成的内容.然后,该模板为列表中的每个项目单独生成相应的编辑器模板 - 在您的情况下,它们都是实例Order,因此,Order模板用于每个项目.

这是神奇的,它很方便,但因为这是按惯例发生的,基本上是隐藏在我们身上的,所以我认为这是混乱的根源.

现在,当您尝试执行相同的操作但通过显式设置使用特定编辑器模板来使用命名模板时,最终会将该编辑器模板传递给整个枚举 - 这就是您发布的错误的来源. EditorForOrderList

换句话说,失败的案例设法跳过工作案例的"魔术"部分,这就是它失败的原因.但是,在语义上它看起来很好听,对吧?有混乱.

工作案例:

your call                                default MVC template      your template
@Html.EditorFor( Model => Model.Orders)  IEnumerable template      Order template
Run Code Online (Sandbox Code Playgroud)

失败案例:

your call                                           your template
@Html.EditorFor(Model=>Model.Orders, "OrderList")   OrderList template       ERROR!!!
Run Code Online (Sandbox Code Playgroud)

有许多方法可以使错误消失,但其中许多方法都存在问题,因为它们会导致HTML控件的呈现方式使您无法通过POST上的索引来解决各个控件.Uhhg.(注意:工作案例确实按预期正确呈现HTML)

为了正确呈现HTML控件,似乎必须使用常规for循环(而不是a foreach)并将每个单独的Order对象传递给自定义模板(我称之为OrderEditorTemplateDefault).

@for (int i = 0; i < Model.Orders.Count ; i++) 
{
    @Html.EditorFor(c => Model.Orders[i], "OrderEditorTemplateDefault")
} 
Run Code Online (Sandbox Code Playgroud)

部分问题表明:

我希望我可以使用相同的子集合将不同的子EditorTemplates用于不同的目的.

你可以通过在循环中引入一个条件并在那里选择备用模板来做到这一点(对于整个列表或逐个订单,只取决于你如何编写条件)

@for (int i = 0; i < Model.Orders.Count ; i++) {
    if (someCondition) {
        @Html.EditorFor(c => Model.Orders[i], "OrderEditorTemplateDefault")
    } else {
        @Html.EditorFor(c => Model.Orders[i], "OrderEditorTemplateALTERNATE")
    }
} 
Run Code Online (Sandbox Code Playgroud)

对不起这么详细.希望有所帮助.

  • 多么精彩而清晰的答案。这里的其他五篇文章并没有真正帮助我解决这个问题,但这让我快速解决了这个问题。 (2认同)