C# 从强类型视图提交多个模型表单时的模型绑定
我在绑定提交了多个模型的表单时遇到问题。我有一个投诉表,其中包括投诉信息以及一对多投诉人。我正试图提交表单,但绑定时出错。ModelState.IsValid始终返回false 如果我调试并查看ModelState错误,我会得到这样一个消息: “EntityCollection已初始化。在反序列化对象图期间,应仅调用InitializeRelatedCollection方法初始化新的EntityCollection” 此外,在调试时,我可以看到投诉模型确实从表单提交中填充了投诉人,因此该部分似乎正在工作 我不确定使用默认的ModelBinder时我所做的是不是不可能的,或者我只是没有按照正确的方式进行。我似乎找不到任何具体的例子或文件。stackoverflow上也存在类似的问题,但它似乎不处理强类型视图 控制器代码:C# 从强类型视图提交多个模型表单时的模型绑定,c#,asp.net-mvc,html-helper,model-binding,C#,Asp.net Mvc,Html Helper,Model Binding,我在绑定提交了多个模型的表单时遇到问题。我有一个投诉表,其中包括投诉信息以及一对多投诉人。我正试图提交表单,但绑定时出错。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();
}
}
视图代码(这是一个由创建/编辑视图调用的局部视图,这些视图也是带有投诉的强类型视图):
收到日期:
输入日期:
得出结论:
事件日期:
姓:
GivenName1:
盲猜:
更改:
<%= Html.TextBox("Complainants[" + i + "].Surname", complainant.Surname)%>
与:
分别-在“投诉人[…”之前添加“投诉”
编辑:
这不是一个正确的答案。请取消删除它,因为这可能会增加一些值,直到弹出正确的答案
EDIT2:
我可能错了,但对我来说,实体框架(或者-您使用它的方式)似乎有问题。我的意思是-asp.net mvc可以从请求中读取值,但无法初始化投诉人集合
上面写着:
InitializeRelatedCollection(TTargetEntity)方法初始化使用默认构造函数创建的现有EntityCollection(TEntity)。EntityCollection(TEntity)使用提供的关系和目标角色名称初始化
InitializeRelatedCollection(TTargetEntity)方法仅在反序列化过程中使用
更多信息:
例外情况:
- 无效操作例外
- 当提供的EntityCollection(TEntity)已初始化时
- 当关系管理器已附加到ObjectContext时
- 当关系管理器已包含具有此名称和目标角色的关系时
这不是针对这个特定问题的解决方案,更像是一种变通方法,一种处理mvc模型部分的正确方法 创建仅用于演示目的的viewmodel。也可以从纯POCO创建新的域模型(因为EF仅在下一版本中支持它们)。用于映射EFDataContextModelViewModel
这需要一些努力,但这是应该如何处理的。这种方法从您的模型中删除表示责任,清理您的域模型(从您的模型中删除EF内容)并且可以解决您的绑定问题。我也有同样的问题!最终您会发现框架无法处理复杂的模型 我编写了一个小绑定组件,可以在post上初始化复杂的绑定
但基本上你要做的是Arnis L.告诉你的。我通过以下操作解决了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)
请记住,如果要绑定多个集合,则需要删除每个集合的错误
在我的实验中,该集合成功地保存了该集合所在图形中的根对象
希望这能帮助别人
public ActionResult Edit([Bind(Exclude = "Complainants")]Complaint model)
{
TryUpdateModel(model.Complainants, "Complainants");
if (!ModelState.IsValid)
{
// return the pre populated model
return View(model);
}
}
这对我有用!
我认为,当投诉对象被创建时,它的“投诉人”集合被初始化(因为实体框架自动逻辑),然后模型绑定器也尝试创建集合本身,这导致了错误。
但是,当我们尝试手动更新模型时,集合已经初始化,但不会要求model binder再次初始化它。要在没有逐案解决方法的情况下使其正常工作,您需要创建自己的model binder并覆盖method 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);
}
}
}
}
}
我已尝试此操作,但ModelState.IsValid仍返回false。ModelState键由符合项[0]填充。姓氏、投诉人[1]。姓氏等。导致其失败的原因是有一个名为投诉人的键。这就是导致我出错的原因“EntityCollection已经初始化。InitializeRelatedCollection方法只应在反序列化对象图期间调用以初始化新的EntityCollection”。使用EntityCollection时可能会出现问题。此链接:breifly介绍了它,但我对它知之甚少
public ActionResult Edit([Bind(Exclude = "Complainants")]Complaint model)
{
TryUpdateModel(model.Complainants, "Complainants");
if (!ModelState.IsValid)
{
// return the pre populated model
return View(model);
}
}
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);
}
}
}
}
}
ModelBinders.Binders.DefaultBinder = new MyDefaultModelBinder();