[英]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.