簡體   English   中英

強類型視圖中多模型表單提交的模型綁定

[英]Model Binding on Multiple Model Form Submission from Strongly-Typed View

我在提交多個模型的表單上遇到問題。 我有一份投訴表格,其中包括投訴信息以及一對多投訴人。 我正在嘗試提交表單,但我在綁定上遇到錯誤。 ModelState.IsValid始終返回false。

如果我調試並查看ModelState錯誤,我會得到一個說法:“EntityCollection已經初始化。應該只調用InitializeRelatedCollection方法在對象圖的反序列化期間初始化一個新的EntityCollection”。

此外,在調試時,我可以看到投訴模型確實填寫了表單提交中的投訴人,因此似乎該部分正在運行。

我不確定使用默認的ModelBinder我是不是可以做什么,或者我是不是以正確的方式去做。 我似乎無法找到任何具體的例子或文件。 這里可以在stackoverflow上找到一個非常類似的問題但它似乎並不處理強類型視圖。

控制器代碼:

    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();
        }
    }

查看代碼(這是由創建/編輯視圖調用的部分視圖,它也是使用Complaint強類型的):

<%@ 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>

盲目猜測:

更改:

<%= 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時。
  • 當關系管理器已經包含與此名稱和目標角色的關系時。

為什么InitializeRelatedCollection會被觸發兩次。 不幸的是 - 我沒有明白的想法為什么。 也許這個小調查對其他人有幫助 - 對EF更有經驗。 :)

EDIT3:
這不是解決這個特定問題的方法,更像是一種解決方法,一種處理mvc模型部分的正確方法。

創建視圖模型僅用於演示目的。 從純POCO創建新的域模型(因為EF將僅在下一版本中支持它們)。 使用AutoMapper映射EFDataContext <=>模型<=> ViewModel。

這需要一些努力,但這應該是如何處理的。 這種方法可以從模型中刪除演示文稿的責任,清除域模型(從模型中刪除EF內容),並通過綁定解決您的問題。

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

}

這適合我!

我認為當Complaint對象被創建時,它的'Complainants'集合被初始化(因為實體框架自動邏輯),然后模型綁定器嘗試創建集合本身,這會導致錯誤。 但是當我們嘗試手動更新模型時,已經初始化了集合,但是不要求模型綁定器再次初始化它。

要在沒有逐案解決方法的情況下使其工作,您需要創建自己的模型綁定器並覆蓋方法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();

我通過執行以下操作解決了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)。

請記住,如果您綁定了多個集合,則需要刪除每個集合的錯誤。

在我的實驗中,集合成功保存以及集合所屬的圖形中的根對象。

希望能幫助別人。

我遇到了同樣的問題! 最后,您會發現框架無法處理復雜的模型。

我寫了一個小的綁定組件,可以初始化帖子上的復雜綁定。

但基本上你要做的就是Arnis L.所說的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM