Maneira correta e idiomática de usar modelos de editor personalizados com modelos IEnumerable no ASP.NET MVC

Esta pergunta é um acompanhamento paraPor que meu DisplayFor não está repetindo meu IEnumerable <DateTime>?

Uma atualização rápida.

Quando:

o modelo possui uma propriedade do tipoIEnumerable<T>você passa essa propriedade paraHtml.EditorFor() usando a sobrecarga que aceita apenas a expressão lambdavocê tem um modelo de editor para o tipoT em Views / Shared / EditorTemplates

o mecanismo MVC chamará automaticamente o modelo do editor para cada item na sequência enumerável, produzindo uma lista dos resultados.

Por exemplo, quando há uma classe de modeloOrder com propriedadeLines:

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

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

E há uma visão Views / Shared / EditorTemplates / OrderLine.cshtml:

@model TestEditorFor.Models.OrderLine

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

Então, quando você invoca@Html.EditorFor(m => m.Lines) na visualização de nível superior, você receberá uma página com caixas de texto para cada linha de pedido, não apenas uma.

No entanto, como você pode ver na pergunta vinculada, isso só funciona quando você usa essa sobrecarga específica deEditorFor. Se você fornecer um nome de modelo (para usar um modelo que não seja nomeado após oOrderLine classe), o tratamento automático de sequência não ocorrerá e ocorrerá um erro de tempo de execução.

Nesse momento, você terá que declarar o modelo do seu modelo personalizado comoIEnumebrable<OrderLine> e iterar manualmente sobre seus itens de uma forma ou de outra para gerar todos eles, por exemplo

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

E é aí que os problemas começam.

Os controles HTML gerados dessa maneira têm todos os mesmos IDs e nomes. Quando você os postar posteriormente, o fichário do modelo não poderá construir uma matriz deOrderLines, e o objeto de modelo que você obtém no método HttpPost no controlador seránull.
Isso faz sentido se você olhar para a expressão lambda - ela realmente não vincula o objeto que está sendo construído a um local no modelo de onde vem.

Tentei várias maneiras de iterar sobre os itens, e parece que a única maneira é redefinir o modelo do modelo comoIList<T> e enumere-o comfor:

@model IList<OrderLine>

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

Em seguida, na visualização de nível superior:

@model TestEditorFor.Models.Order

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

que fornece controles HTML nomeados corretamente e reconhecidos corretamente pelo fichário do modelo em um envio.

Enquanto isso funciona, parece muito errado.

Qual é a maneira correta e idiomática de usar um modelo de editor personalizado comEditorFor, preservando todos os links lógicos que permitem que o mecanismo gere HTML adequado para o fichário do modelo?

questionAnswers(4)

yourAnswerToTheQuestion