型号从强类型的视图绑定多模型提交表单(Model Binding on Multiple Model

2019-07-29 11:09发布

我有问题提交多个模型的窗体上绑定。 我有一个投诉表格,其中包括投诉信息以及一个一对多的投诉。 我想提交表单,但我对绑定得到错误。 ModelState.IsValid始终返回false。

如果我调试和查看的ModelState错误,我得到一个说法:“在EntityCollection已经初始化的InitializeRelatedCollection方法应该只能被称为一个对象图的反序列化过程中初始化新EntityCollection”。

此外,调试时,我可以看到投诉模型并得到与投诉人从表单提交填充,因此它似乎部分工作。

我不知道我在做什么是不可能的默认模型绑定器,或者如果我根本不会去了解它的正确方法。 我似乎无法找到任何这具体的例子或文件。 一个非常类似的问题可以在计算器中找到这里 ,但它似乎不处理强类型的意见。

控制器代码:

    public ActionResult Edit(int id)
    {
        var complaint = (from c in _entities.ComplaintSet.Include("Complainants")
                     where c.Id == id
                     select c).FirstOrDefault();

        return View(complaint);
    }

    //
    // POST: /Home/Edit/5
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Edit(Complaint complaint)
    {
        if (!ModelState.IsValid)
        {
            return View();
        }
        try
        {
            var originalComplaint = (from c in _entities.ComplaintSet.Include("Complainants")
                                     where c.Id == complaint.Id
                                     select c).FirstOrDefault();
            _entities.ApplyPropertyChanges(originalComplaint.EntityKey.EntitySetName, complaint);
            _entities.SaveChanges();
            return RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
    }

查看代码(这是获取通过创建/编辑意见,这也强烈投诉类型称为局部视图):

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ProStand.Models.Complaint>" %>

<%= Html.ValidationSummary() %>
<% using (Html.BeginForm()) {%>

<table cellpadding="0" cellspacing="0" class="table">
    <tr>
        <td>
        <label for="DateReceived">Date Received:</label>
            <%= Html.TextBox("DateReceived") %>
            <%= Html.ValidationMessage("DateReceived", "*") %>    
        </td>
        <td>
            <label for="DateEntered">Date Entered:</label>
            <%= Html.TextBox("DateEntered")%>
            <%= Html.ValidationMessage("DateEntered", "*") %>
        </td>
    </tr>
    <tr>
        <td>
            <label for="Concluded">Concluded:</label>
            <%= Html.CheckBox("Concluded")%>
            <%= Html.ValidationMessage("Concluded", "*") %>
            </td>
        <td>
            <label for="IncidentDate">Incident Date:</label>
            <%= Html.TextBox("IncidentDate")%>
            <%= Html.ValidationMessage("IncidentDate", "*") %></td>
    </tr>
</table>
    <hr />
    <table>
    <% if (Model != null) {
           int i = 0;
       foreach (var complainant in Model.Complainants){ %>
       <%= Html.Hidden("Complainants[" + i + "].Id", complainant.Id)%>
    <tr>
        <td>
            <label for="Surname">Surname:</label>

            <%= Html.TextBox("Complainants[" + i + "].Surname", complainant.Surname)%>
            <%= Html.ValidationMessage("Surname", "*")%>
        </td>
        <td>
            <label for="GivenName1">GivenName1:</label>
            <%= Html.TextBox("Complainants[" + i + "].GivenName1", complainant.GivenName1)%>
            <%= Html.ValidationMessage("GivenName1", "*")%>
        </td>
    </tr>
    <% i++; %>
    <% }} %>
    <tr>
        <td colspan=2>
            <input type="submit" value="Submit" />
        </td>
    </tr>
</table>
<% } %>
<div>
    <%=Html.ActionLink("Back to List", "Index") %>
</div>

Answer 1:

盲目的猜测:

更改:

<%= Html.TextBox("Complainants[" + i + "].Surname", complainant.Surname)%>

有:

<%= Html.TextBox("Complaint.Complainants[" + i + "].Surname",  
complainant.Surname)%>

分别 - 添加“的投诉。” 之前的“投诉[...”

编辑

这不是一个正确的答案。 离开它未删除,只是因为直到正确答案弹出,可能增加一些价值。

EDIT2:

我可能是错的,但对我来说,似乎有一个与实体框架问题(或 - 与你使用它的方式)。 我的意思是 - asp.net mvc的管理,以读取请求的值,但不能初始化投诉收集。

在这里它写成:

所述InitializeRelatedCollection(TTargetEntity)方法初始化是通过使用默认构造创建的现有EntityCollection(TEntity)已。 该EntityCollection(TEntity)已被使用提供的关系和目标角色名称初始化。

所述InitializeRelatedCollection(TTargetEntity)方法仅反序列化过程中使用。

一些更多的信息:

例外:

  • 出现InvalidOperationException

条件:

  • 当提供EntityCollection(TEntity)已已初始化。
  • 当关系经理已经连接到ObjectContext的。
  • 当关系经理已经包含与此名称和目标角色的关系。

Somewhy InitializeRelatedCollection得到射击两次。 不幸的 - 我没有好主意到底为什么。 也许这个小调查将帮助别人 - 更经历了EF。 :)

EDIT3:
这是不是为这个特殊问题的解决方案,更像是一种解决方法,来处理MVC模型部分之有道。

创建仅用于演示目的的视图模型。 创建一个从纯波苏斯一个新的域模型太(因为EF会支持他们在仅次于版)。 使用AutoMapper映射EFDataContext <=>型号<=>视图模型。

这将需要一些努力,但是这是它应该如何处理。 这种方法将删除你的模型演示的责任,清理你的域模型(从模型中删除EF的东西),将与结合解决您的问题。



Answer 2:

public ActionResult Edit([Bind(Exclude = "Complainants")]Complaint model)
{
  TryUpdateModel(model.Complainants, "Complainants");
  if (!ModelState.IsValid)
  {
      // return the pre populated model
      return View(model);
  }

}

这对我的作品!

我认为,投诉对象被创建时,那么它的“投诉”集合被初始化(因为实体框架自动逻辑的),然后模型粘结剂试图创建集合本身为好,这将导致错误。 但是,当我们试图更新模型,然后手动收集已初始化,但模型绑定不要求再次初始化。



Answer 3:

为了得到这个工作,而不逐案变通方法,你需要创建自己的模型粘结剂和覆盖方法的SetProperty:

public class MyDefaultModelBinder : DefaultModelBinder
{
    protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, object value)
    { 
        ModelMetadata propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name]; 
        propertyMetadata.Model = value;
        string modelStateKey = CreateSubPropertyName(bindingContext.ModelName, propertyMetadata.PropertyName);

        // Try to set a value into the property unless we know it will fail (read-only 
        // properties and null values with non-nullable types)
        if (!propertyDescriptor.IsReadOnly) { 
        try {
            if (value == null)
            {
            propertyDescriptor.SetValue(bindingContext.Model, value);
            }
            else
            {
            Type valueType = value.GetType();

            if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(EntityCollection<>))
            {
                IListSource ls = (IListSource)propertyDescriptor.GetValue(bindingContext.Model);
                IList list = ls.GetList();

                foreach (var item in (IEnumerable)value)
                {
                list.Add(item);
                }
            }
            else
            {
                propertyDescriptor.SetValue(bindingContext.Model, value);
            }
            }

        }
        catch (Exception ex) {
            // Only add if we're not already invalid
            if (bindingContext.ModelState.IsValidField(modelStateKey)) { 
            bindingContext.ModelState.AddModelError(modelStateKey, ex); 
            }
        } 
        }
    }
}

不要忘记在Global.asax中注册您的粘结剂:

ModelBinders.Binders.DefaultBinder = new MyDefaultModelBinder();


Answer 4:

我工作的ModelBinding例外各地通过执行以下操作:

// Remove the error from ModelState which will have the same name as the collection.
ModelState.Remove("Complaints"/*EntityCollection*/); 
if (ModelState.IsValid) // Still catches other errors.
{
    entities.SaveChanges(); // Your ObjectContext
}

其主要缺点是异常还是抛出,并且可以在运行时是昂贵的。 优雅的解决办法可能是创建围绕现有DefaultBinder的包装,并防止再次实例化EntityCollection,这已经是由EF完成。 然后结合该集合的形式值(的FormCollection)。

请记住,如果你要绑定多个集合,你将需要删除错误的每个集合。

在我的实验,收集保存的成功,以及在资源集合的一部分,图中的根对象。

希望可以帮助别人。



Answer 5:

我有相同的问题! 最后,你会发现,该框架不能处理复杂的模型。

我写的,可以在初始化后的复杂绑定有点绑定组件。

但基本上你需要做的是什么就是什么阿尔尼斯L.告诉。



文章来源: Model Binding on Multiple Model Form Submission from Strongly-Typed View