小编典典

在ASP.NET MVC中将自定义编辑器模板与IEnumerable模型一起使用的正确,惯用方式

c#

这个问题是 为什么我的DisplayFor不循环通过IEnumerable
的后续问题


快速刷新。

什么时候:

  • 模型具有类型的属性 IEnumerable<T>
  • 您将此属性传递给Html.EditorFor()使用仅接受lambda表达式的重载
  • T在Views / Shared / EditorTemplates下具有该类型的编辑器模板

然后MVC引擎将自动为可枚举序列中的每个项目调用编辑器模板,并生成结果列表。

例如,当存在一个Order具有property 的模型类时Lines

public class Order
{
    public IEnumerable<OrderLine> Lines { get; set; }
}

public class OrderLine
{
    public string Prop1 { get; set; }
    public int Prop2 { get; set; }
}

并且有一个视图Views / Shared / EditorTemplates / OrderLine.cshtml:

@model TestEditorFor.Models.OrderLine

@Html.EditorFor(m => m.Prop1)
@Html.EditorFor(m => m.Prop2)

然后,当您@Html.EditorFor(m => m.Lines)从顶层视图调用时,您将获得一个页面,其中包含每个订单行的文本框,而不仅仅是一个。


但是,正如您在链接的问题中看到的那样,这仅在您使用的特定重载时才有效EditorFor。如果提供模板名称(为了使用未在OrderLine类后命名的模板),则不会发生自动序列处理,而是会发生运行时错误。

此时,您将必须将自定义模板的模型声明为,IEnumebrable<OrderLine>并以某种方式手动遍历其项目以输出所有项目,例如

@foreach (var line in Model.Lines) {
    @Html.EditorFor(m => line)
}

这就是问题开始的地方。

以这种方式生成的HTML控件均具有相同的ID和名称。当您稍后对其进行POST时,模型绑定程序将无法构造OrderLines
的数组,而您在控制器的HttpPost方法中获得的模型对象将为null
如果看一下lambda表达式,这是有道理的-它实际上并没有将正在构造的对象链接到它所来自的模型中的某个位置。

我尝试了各种遍历项目的方法,似乎唯一的方法是将模板的模型重新声明为,IList<T>并使用进行枚举for

@model IList<OrderLine>

@for (int i = 0; i < Model.Count(); i++)
{ 
    @Html.EditorFor(m => m[i].Prop1)
    @Html.EditorFor(m => m[i].Prop2)
}

然后在顶级视图中:

@model TestEditorFor.Models.Order

@using (Html.BeginForm()) {
    @Html.EditorFor(m => m.Lines, "CustomTemplateName")
}

它提供了正确命名的HTML控件,这些模型控件可以在提交时由模型绑定程序正确识别。


虽然这样做有效,但感觉却很不对劲。

使用带有的自定义编辑器模板的正确,惯用方式是什么EditorFor,同时保留所有允许引擎生成适合于模型绑定器的HTML的逻辑链接?


阅读 282

收藏
2020-05-19

共1个答案

小编典典

与Erik
Funkenbusch讨论
之后,导致研究了MVC源代码,看来有两种更好的方法(正确和惯用的吗?)。

两者都涉及为助手提供正确的html名称前缀,并生成与default的输出相同的HTML EditorFor

我现在将其保留在此处,将进行更多测试,以确保它在深度嵌套的场景中可以工作。

对于以下示例,假设您已经有两个用于OrderLine类的模板:OrderLine.cshtmlDifferentOrderLine.cshtml


方法1-使用中间模板 IEnumerable<T>

创建一个帮助器模板,将其保存为任何名称(例如“ ManyDifferentOrderLines.cshtml”):

@model IEnumerable<OrderLine>

@{
    int i = 0;

    foreach (var line in Model)
    { 
        @Html.EditorFor(m => line, "DifferentOrderLine", "[" + i++ + "]")
    }
}

然后从主订单模板调用它:

@model Order

@Html.EditorFor(m => m.Lines, "ManyDifferentOrderLines")

方法2-不使用中间模板 IEnumerable<T>

在主订单模板中:

@model Order

@{
    int i = 0;

    foreach (var line in Model.Lines)
    {
        @Html.EditorFor(m => line, "DifferentOrderLine", "Lines[" + i++ + "]")
    }
}
2020-05-19