C# 为什么DbContext.Entry(IEnumerable<;MedicalProduct>;).State在我的代码中生成ArguementNullException?

C# 为什么DbContext.Entry(IEnumerable<;MedicalProduct>;).State在我的代码中生成ArguementNullException?,c#,asp.net-mvc,entity-framework,asp.net-mvc-4,razor,C#,Asp.net Mvc,Entity Framework,Asp.net Mvc 4,Razor,在我的MedicalProductController中,我试图使我的Edit操作能够在一个页面上编辑多个对象。为此,我计划使用HTTPPOSTedit操作方法接收IEnumerable,而不是脚手架为我设置的MedicalProduct 当我单击save提交一些更改时,我在以下行中得到一个未处理的ArguementNullException\u db.Entry(productList).State=EntityState.ModifiedmodelItem.ID) @LabelFor(ite

在我的
MedicalProductController
中,我试图使我的
Edit
操作能够在一个页面上编辑多个对象。为此,我计划使用
HTTPPOST
edit操作方法接收
IEnumerable
,而不是脚手架为我设置的
MedicalProduct

当我单击save提交一些更改时,我在以下行中得到一个未处理的
ArguementNullException
\u db.Entry(productList).State=EntityState.Modified 医疗产品控制器:
公共类MedicalProductController:控制器
{
私有MvcMedicalStoreDb_db=新的MvcMedicalStoreDb();
//为了简洁起见,省略了一些代码
公共操作结果编辑(int id=0)
{
MedicalProduct product=\u db.Products.Find(id);
如果(产品==null)
{
返回HttpNotFound();
}
var productList=新列表{product};
var viewModel=GetMedicalProductViewModelList(productList);
返回视图(viewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
公共操作结果编辑(IEnumerable productList)
{
if(ModelState.IsValid)
{
_db.Entry(productList).State=EntityState.Modified;
_db.SaveChanges();
返回操作(“索引”);
}
//var productList=新列表{product};
var viewModel=GetMedicalProductViewModelList(productList);
返回视图(viewModel);
}
}
Edit.cshtml:
@model IEnumerable
@{
ViewBag.Title=“编辑”;
}
编辑
@使用(Html.BeginForm()){
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
医药产品
@foreach(模型中的var modelItem)
{
@Html.HiddenFor(item=>modelItem.ID)
@LabelFor(item=>modelItem.Name)
@EditorFor(item=>modelItem.Name)
@Html.ValidationMessageFor(item=>modelItem.Name)
@LabelFor(item=>modelItem.Price)
@EditorFor(item=>modelItem.Price)
@Html.ValidationMessageFor(item=>modelItem.Price)
@LabelFor(item=>modelItem.BrandName)
@Html.DropDownListFor(item=>modelItem.BrandName,modelItem.BrandSelectListItem)
@Html.ValidationMessageFor(item=>modelItem.BrandName)
}

} @ActionLink(“返回列表”、“索引”) @节脚本{ @Scripts.Render(“~/bundles/jqueryval”) }
在我看来,模型绑定器无法绑定到您的收藏,这将导致它
为null
。它这样做的原因是您没有为每个元素指定索引。这意味着MVC无法确定如何正确绑定它们

编辑 我已经弄明白了为什么这个答案的最后一次修订不起作用。首先,
IEnumerable
没有直接索引器。相反,您可以使用
Model.ElementAt(i).ID
访问
ID
属性。但是,这实际上并不能解决模型绑定问题,因为出于某种原因,这不会为生成的
字段在
名称
属性上生成适当的索引。(更多信息请参见下文。)

有两种方法可以解决这个问题。第一种方法是将
列表
传递给视图,而不是
IEnumerable
,然后访问前面显示的字段。但是,更好的方法是创建一个
EditorTemplate
。这将更容易,因为它使您无需更改生成视图模型的现有方法。因此,您需要遵循以下步骤:

  • 在视图的当前文件夹中创建一个
    EditorTemplates
    文件夹(例如,如果视图是
    Home\Index.cshtml
    ,则创建文件夹
    Home\EditorTemplates
  • 在该目录中创建一个强类型视图,其名称与您的模型匹配(例如,在本例中,该视图将被称为
    MedicalProductViewModel
  • 将原始视图的大部分移动到新模板中
  • 您将得到以下结果:

    @model MedicalProductViewModel
    
    @Html.HiddenFor(item => Model.ID)
    
    <div class="editor-label">
        @Html.LabelFor(item => Model.Name)
    </div>
    <div class="editor-field">
        @Html.EditorFor(item => Model.Name)
        @Html.ValidationMessageFor(item => Model.Name)
    </div>
    
    <div class="editor-label">
        @Html.LabelFor(item => Model.Price)
    </div>
    <div class="editor-field">
        @Html.EditorFor(item => Model.Price)
        @Html.ValidationMessageFor(item => Model.Price)
    </div>
    
    <div class="editor-label">
        @Html.LabelFor(item => Model.BrandName)
    </div>
    <div class="editor-field">
        @Html.DropDownListFor(item => Model.BrandName, Model.BrandSelectListItem)
        @Html.ValidationMessageFor(item => Model.BrandName)
    </div>
    
    <input name="ID" type="text" value="1" />
    <input name="Name" type="text" value="Name 1" />
    <input name="ID" type="text" value="2" />
    <input name="Name" type="text" value="Name 2" />
    
    虽然我在开始时做了一个简短的解释,但我真的应该解释一下这到底在做什么。原始HTML将产生如下输出:

    @model MedicalProductViewModel
    
    @Html.HiddenFor(item => Model.ID)
    
    <div class="editor-label">
        @Html.LabelFor(item => Model.Name)
    </div>
    <div class="editor-field">
        @Html.EditorFor(item => Model.Name)
        @Html.ValidationMessageFor(item => Model.Name)
    </div>
    
    <div class="editor-label">
        @Html.LabelFor(item => Model.Price)
    </div>
    <div class="editor-field">
        @Html.EditorFor(item => Model.Price)
        @Html.ValidationMessageFor(item => Model.Price)
    </div>
    
    <div class="editor-label">
        @Html.LabelFor(item => Model.BrandName)
    </div>
    <div class="editor-field">
        @Html.DropDownListFor(item => Model.BrandName, Model.BrandSelectListItem)
        @Html.ValidationMessageFor(item => Model.BrandName)
    </div>
    
    <input name="ID" type="text" value="1" />
    <input name="Name" type="text" value="Name 1" />
    <input name="ID" type="text" value="2" />
    <input name="Name" type="text" value="Name 2" />
    
    如您所见,这些字段现在有一个与之关联的索引。这为模型绑定器提供了将所有项目添加到集合中所需的所有信息。现在这已经不重要了,我们可以继续修复您的产品保存代码

    Gert所说的关于您试图保存
    productList
    的方式仍然是正确的。您需要在该集合中的每个项目上设置
    EntityState.Modified
    标志:

    if (ModelState.IsValid)
    {
        foreach (var product in productList)
            _db.Entry(product).State = EntityState.Modified;
    
        _db.SaveChanges();
    
        return RedirectToAction("Index");
    }
    

    看看这是否有效。

    我本以为会出现
    invalidoOperationException
    声明
    productList
    不是模型的一部分。因为这似乎是错误的:您应该设置单个
    产品的状态。当我在cshtml文件中使用该代码时,我得到一个编译错误:>无法将带[]的索引应用于“System.Collections.Generic.IEnumerable”类型的表达式@ArmorCode更新了我的答案。希望这对你现在有用。我不确定我是否完全理解。编译器如何知道将编辑器模板文件中的内容放入
    Html.Editorfor(m=>m)
    ?我的理解是,强类型的
    @model IEnumerable
    告诉视图,对于g
    <input name="[0].ID" type="text" value="1" />
    <input name="[0].Name" type="text" value="Name 1" />
    <input name="[1].ID" type="text" value="2" />
    <input name="[1].Name" type="text" value="Name 2" />
    
    if (ModelState.IsValid)
    {
        foreach (var product in productList)
            _db.Entry(product).State = EntityState.Modified;
    
        _db.SaveChanges();
    
        return RedirectToAction("Index");
    }