Razor 编辑和保存复杂对象

Razor 编辑和保存复杂对象,razor,asp.net-mvc-4,asp.net-mvc-templates,Razor,Asp.net Mvc 4,Asp.net Mvc Templates,假设您有以下对象: public class Address { public String Line1 { get; set; } public String Line2 { get; set; } public String City { get; set; } public String State { get; set; } public String ZipCode { get; set; } public Address() { } } publi

假设您有以下对象:

public class Address
{
  public String Line1 { get; set; }
  public String Line2 { get; set; }
  public String City { get; set; }
  public String State { get; set; }
  public String ZipCode { get; set; }

  public Address()
  {
  }
}

public class Contact
{
  public String FirstName { get; set; }
  public String LastName { get; set; }
  public String Telephone { get; set; }

  public Address BillingAddress { get; set; }
  public List<Address> ShippingAddresses { get; set; }

  public Contact()
  {
    // Assume anything that _could_ be null wouldn't be. I'm excluding
    // most "typical" error checking just to keep the examples simple
    this.BillingAddress = new Address();
    this.ShippingAddresses = new List<Address>();
  }
}
我一直看到在MVC中编辑这样一个对象的演示,但他们总是以自己的形式将对象集合分解(例如,编辑联系人,然后编辑联系人的特定地址)。一、 另一方面,我试图在同一页中编辑所有这些信息,但没有成功。例如:

@model Contact

// simplified for brevity
@using (Html.BeginForm())
{
  @Html.LabelFor(x => x.FirstName): @Html.EditorFor(x => x.FirstName)
  @Html.LabelFor(x => x.LastName): @Html.EditorFor(x => x.LastName)
  @Html.LabelFor(x => x.Telephone): @Html.EditorFor(x => x.Telephone)

  <div>
    @Html.LabelFor(x => x.BillingAddress.Line1): @Html.EditorFor(x => x.BillingAddress.Line1)
    @Html.LabelFor(x => x.BillingAddress.Line2): @Html.EditorFor(x => x.BillingAddress.Line2)
    @Html.LabelFor(x => x.BillingAddress.City): @Html.EditorFor(x => x.BillingAddress.City)
    @Html.LabelFor(x => x.BillingAddress.State): @Html.EditorFor(x => x.BillingAddress.State)
    @Html.LabelFor(x => x.BillingAddress.ZipCode): @Html.EditorFor(x => x.BillingAddress.ZipCode)
  </div>

  <div>
  @foreach (Address addr in Model.ShippingAddresses)
  {
    <div>
      @Html.LabelFor(x => addr.Line1): @Html.EditorFor(x => addr.Line1)
      @Html.LabelFor(x => addr.Line2): @Html.EditorFor(x => addr.Line2)
      @Html.LabelFor(x => addr.City): @Html.EditorFor(x => addr.City)
      @Html.LabelFor(x => addr.State): @Html.EditorFor(x => addr.State)
      @Html.LabelFor(x => addr.ZipCode): @Html.EditorFor(x => addr.ZipCode)
    </div>
  }
  </div>
}
@model联系人
//为简洁起见简化
@使用(Html.BeginForm())
{
@LabelFor(x=>x.FirstName):@Html.EditorFor(x=>x.FirstName)
@LabelFor(x=>x.LastName):@Html.EditorFor(x=>x.LastName)
@LabelFor(x=>x.Telephone):@Html.EditorFor(x=>x.Telephone)
@LabelFor(x=>x.BillingAddress.Line1):@Html.EditorFor(x=>x.BillingAddress.Line1)
@LabelFor(x=>x.BillingAddress.Line2):@Html.EditorFor(x=>x.BillingAddress.Line2)
@LabelFor(x=>x.BillingAddress.City):@Html.EditorFor(x=>x.BillingAddress.City)
@LabelFor(x=>x.BillingAddress.State):@Html.EditorFor(x=>x.BillingAddress.State)
@LabelFor(x=>x.BillingAddress.ZipCode):@Html.EditorFor(x=>x.BillingAddress.ZipCode)
@foreach(模型中的地址addr.ShippingAddresses)
{
@LabelFor(x=>addr.Line1):@Html.EditorFor(x=>addr.Line1)
@LabelFor(x=>addr.Line2):@Html.EditorFor(x=>addr.Line2)
@LabelFor(x=>addr.City):@Html.EditorFor(x=>addr.City)
@LabelFor(x=>addr.State):@Html.EditorFor(x=>addr.State)
@LabelFor(x=>addr.ZipCode):@Html.EditorFor(x=>addr.ZipCode)
}
}
我一直在运行的问题是,当我返回保存信息时,
ModelState.IsValid
从未经过。做这件事有什么诀窍吗,还是仅仅超出了MVC的范畴?我想使用像
联系人
这样的对象,将所有信息转储到一个页面上进行编辑,并成功地重新保存,但我似乎无法使其正常工作。(我的下一步是加入ajax,这样您就可以在该页面上动态添加/删除“ShippingAddress”,但我需要先保存才能工作——K.I.S.S)

问题:

  • ModelState.IsValid
    几乎总是false
  • 集合项的表单元素通常具有相同的名称,因此在本演示中,
    ShippingAddresses
    集合中的每个
    Line1
    都会作为
    name=“addr\u Line1”
    转储到页面,而不是像我所期望的那样

我猜
ModelState.IsValid
为false,因为您没有正确填充shipping addresses集合。发生这种情况是因为您没有为输入字段使用专有名称。请查看,以更好地了解模型绑定器希望您的输入字段命名为什么格式,以便能够重新构造值

代码未生成正确的输入名称的原因是使用了此foreach循环,其中视图失去了导航上下文的跟踪

所以试着这样做:

@for (var i = 0; i < Model.ShippingAddresses.Count; i++)
{
    <div>
        @Html.LabelFor(x => x.ShippingAddresses[i].Line1): 
        @Html.EditorFor(x => x.ShippingAddresses[i].Line1)

        @Html.LabelFor(x => x.ShippingAddresses[i].Line2): 
        @Html.EditorFor(x => x.ShippingAddresses[i].Line2)

        @Html.LabelFor(x => x.ShippingAddresses[i].City): 
        @Html.EditorFor(x => x.ShippingAddresses[i].City)

        @Html.LabelFor(x => x.ShippingAddresses[i].State): 
        @Html.EditorFor(x => x.ShippingAddresses[i].State)

        @Html.LabelFor(x => x.ShippingAddresses[i].ZipCode): 
        @Html.EditorFor(x => x.ShippingAddresses[i].ZipCode)
    </div>    
}
@model Contact

@using (Html.BeginForm())
{
    @Html.LabelFor(x => x.FirstName): 
    @Html.EditorFor(x => x.FirstName)

    @Html.LabelFor(x => x.LastName): 
    @Html.EditorFor(x => x.LastName)

    @Html.LabelFor(x => x.Telephone): 
    @Html.EditorFor(x => x.Telephone)

    @Html.EditorFor(x => x.BillingAddress)

    @Html.EditorFor(x => x.ShippingAddresses)
}
然后定义一个自定义编辑器模板,该模板将自动为
ShippingAddresses
集合(
~/Views/Shared/EditorTemplates/Address.cshtml
)的每个元素呈现:

@型号地址
@Html.LabelFor(x=>x.Line1):
@EditorFor(x=>x.Line1)
@Html.LabelFor(x=>x.Line2):
@EditorFor(x=>x.Line2)
@Html.LabelFor(x=>x.City):
@EditorFor(x=>x.City)
@Html.LabelFor(x=>x.State):
@EditorFor(x=>x.State)
@Html.LabelFor(x=>x.ZipCode):
@EditorFor(x=>x.ZipCode)

现在,您不必再担心输入名称错误。不仅如此,您还在为账单地址和发货地址集合重用地址编辑器模板。它使您的视图更干燥。

您可以将
[AcceptVerbs(HttpVerbs.Post)]
缩短为
[HttpPost]
Contact是否有默认构造函数?@ChrisS:是的,为了参数起见,它们都有空构造函数。看,这是关于MVC和模板的一些我还没有完全理解的东西;如果我想让元素有“合理”的名称(即并非所有元素都被命名为同一事物),lamba在其中起着重要作用?@bradcristie,这绝对是至关重要的。但如果遵循以下简单规则,您将始终正确:如果您发现自己在视图中编写循环(
for
foreach
),那么您就做错了。警报应立即响起。因此,停止并用模板替换此循环(如果以只读方式显示数据,则使用编辑器,如果以只读方式显示数据,则显示)。跟进:假设我没有使用列表,而是使用“地址”自定义集合。模板是否仍然允许这种灵活性?e、 g.
MyPage.cshtml
=>x.Addresses)--
Addresses.cshtml
=>
for(i…{@Html.EditorFor(model=>model[i])
--(然后
Address.cshtml
模板,如您所述?)@BradChristie,为什么你还在为循环显示?我对循环说了不。模板绝对适用于任何集合(自定义或非自定义),而不仅仅是集合,任何类型。因此,如果我们假设在视图模型上有一个属性
IEnumerable FooBars{get;set;}
,那么在主视图中就不会编写循环(记住这一点),只需编写
@Html.EditorFor(x=>x.FooBars)
然后在
~/Views/Shared/EditorTemplates/MyElement.cshtml
中定义一个自定义编辑器模板,该模板将为此集合的每个元素自动呈现……模板的位置和名称很重要。它应该位于
~/Views/Shared/EditorTemplates
~/Views/XXX/Ed>中itorTemplates
其中XXX是当前控制器的名称。在第一种情况下,您定义的模板可以是
@model Contact

@using (Html.BeginForm())
{
    @Html.LabelFor(x => x.FirstName): 
    @Html.EditorFor(x => x.FirstName)

    @Html.LabelFor(x => x.LastName): 
    @Html.EditorFor(x => x.LastName)

    @Html.LabelFor(x => x.Telephone): 
    @Html.EditorFor(x => x.Telephone)

    @Html.EditorFor(x => x.BillingAddress)

    @Html.EditorFor(x => x.ShippingAddresses)
}
@model Address
<div>
    @Html.LabelFor(x => x.Line1): 
    @Html.EditorFor(x => x.Line1)

    @Html.LabelFor(x => x.Line2): 
    @Html.EditorFor(x => x.Line2)

    @Html.LabelFor(x => x.City): 
    @Html.EditorFor(x => x.City)

    @Html.LabelFor(x => x.State): 
    @Html.EditorFor(x => x.State)

    @Html.LabelFor(x => x.ZipCode): 
    @Html.EditorFor(x => x.ZipCode)
</div>