MVC5/实体| Update/Create 复杂模型
MVC5 / Entity | Update/Create Complex Model
我创建了一个复杂的模型,并且一直在努力寻找将表单数据 add/update 到数据库的方法。
基本上我的模型如下所示:
namespace Models
{
public class ModelClass1
{
public int Id { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public ICollection<ModelClass2> ModelClass2 { get; set; }
}
public class ModelClass2
{
public int Id { get; set; }
public string Prop3 { get; set; }
public string Prop4 { get; set; }
public ICollection<ModelClass3> ModelClass3 { get; set; }
}
public class ModelClass3
{
public int Id { get; set; }
public string Prop5 { get; set; }
public string Prop6 { get; set; }
}
}
我已经为 ModelClass1 创建了一个脚手架 CRUD 控制器,并尝试使用一个编辑更新所有 3 个实体 class。
我的编辑 (get/post) classes :
// GET
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
ModelClass1 ModelClass1 = db.ModelClass1
.Include(i => i.ModelClass2.Select(c => c.ModelClass3))
.Where(x => x.Id == id)
.Single();
if (config == null)
{
return HttpNotFound();
}
return View(ModelClass1);
}
// POST
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ModelClass1 ModelClass1)
{
if (ModelState.IsValid)
{
db.Entry(ModelClass1).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(ModelClass1);
}
我选择使用编辑器模板来填充编辑视图中的表单,因此 Edi.cshtml 将类似于:
@model ModelClass1
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.Id)
<div class="form-group">
@Html.LabelFor(model => model.Prop1, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prop1, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Prop1, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prop2, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prop2, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Prop2, "", new { @class = "text-danger" })
</div>
</div>
@Html.EditorFor(model => model.ModelClass2, new { htmlAttributes = new { @class = "form-control" } })
@Html.EditorFor(model => model.ModelClass2.FirstOrDefault().ModelClass3, new { htmlAttributes = new { @class = "form-control" } })
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
}
并为我的 3 个模型中的每一个创建了模板 class 类似于下面的模板:
@model ModelClass2
<div class="form-group">
@Html.LabelFor(model => model.Prop3, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prop3, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Prop3, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prop4, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prop4, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Prop4, "", new { @class = "text-danger" })
</div>
</div>
数据显示正确,然后我调用 Edit 控制器,但是当我修改表单中的数据并且 post 它调用 HTTPPost Edit 方法时,我收到以下错误:
Attaching an entity of type 'ModelClass3' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.
如果我从控制器/视图中完全删除 ModelClass3,我不会收到任何错误,但修改后的数据不会保存到数据库中。
我很确定我的方法不是最好的方法,所以任何帮助将不胜感激,因为我 运行 没有想法。
首先,我不建议直接在前端使用您的域实体。我会为您的域实体创建一个视图模型 (VM)。无论如何,
您报告的这个错误可能是由于实体没有附加到上下文。所以你应该在将状态设置为 Modified 之前附加它:
// POST
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ModelClass1 ModelClass1)
{
if (ModelState.IsValid)
{
db.Attach(ModelClass1);
db.Entry(ModelClass1).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(ModelClass1);
}
但是我更愿意先从数据库中获取实体,然后更新属性:
// POST
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ModelClass1 ModelClass1)
{
if (ModelState.IsValid)
{
ModelClass1 entityFromDB = db.ModelClass1
.Include(i => i.ModelClass2.Select(c => c.ModelClass3))
.Where(x => x.Id == id)
.Single();
MapProperties(entityFromDB, ModelClass1); // You could use a mapper (Auto Mapper)
db.Entry(ModelClass1).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(ModelClass1);
}
我创建了一个复杂的模型,并且一直在努力寻找将表单数据 add/update 到数据库的方法。
基本上我的模型如下所示:
namespace Models
{
public class ModelClass1
{
public int Id { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public ICollection<ModelClass2> ModelClass2 { get; set; }
}
public class ModelClass2
{
public int Id { get; set; }
public string Prop3 { get; set; }
public string Prop4 { get; set; }
public ICollection<ModelClass3> ModelClass3 { get; set; }
}
public class ModelClass3
{
public int Id { get; set; }
public string Prop5 { get; set; }
public string Prop6 { get; set; }
}
}
我已经为 ModelClass1 创建了一个脚手架 CRUD 控制器,并尝试使用一个编辑更新所有 3 个实体 class。
我的编辑 (get/post) classes :
// GET
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
ModelClass1 ModelClass1 = db.ModelClass1
.Include(i => i.ModelClass2.Select(c => c.ModelClass3))
.Where(x => x.Id == id)
.Single();
if (config == null)
{
return HttpNotFound();
}
return View(ModelClass1);
}
// POST
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ModelClass1 ModelClass1)
{
if (ModelState.IsValid)
{
db.Entry(ModelClass1).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(ModelClass1);
}
我选择使用编辑器模板来填充编辑视图中的表单,因此 Edi.cshtml 将类似于:
@model ModelClass1
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.Id)
<div class="form-group">
@Html.LabelFor(model => model.Prop1, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prop1, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Prop1, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prop2, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prop2, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Prop2, "", new { @class = "text-danger" })
</div>
</div>
@Html.EditorFor(model => model.ModelClass2, new { htmlAttributes = new { @class = "form-control" } })
@Html.EditorFor(model => model.ModelClass2.FirstOrDefault().ModelClass3, new { htmlAttributes = new { @class = "form-control" } })
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
}
并为我的 3 个模型中的每一个创建了模板 class 类似于下面的模板:
@model ModelClass2
<div class="form-group">
@Html.LabelFor(model => model.Prop3, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prop3, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Prop3, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Prop4, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Prop4, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Prop4, "", new { @class = "text-danger" })
</div>
</div>
数据显示正确,然后我调用 Edit 控制器,但是当我修改表单中的数据并且 post 它调用 HTTPPost Edit 方法时,我收到以下错误:
Attaching an entity of type 'ModelClass3' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.
如果我从控制器/视图中完全删除 ModelClass3,我不会收到任何错误,但修改后的数据不会保存到数据库中。
我很确定我的方法不是最好的方法,所以任何帮助将不胜感激,因为我 运行 没有想法。
首先,我不建议直接在前端使用您的域实体。我会为您的域实体创建一个视图模型 (VM)。无论如何,
您报告的这个错误可能是由于实体没有附加到上下文。所以你应该在将状态设置为 Modified 之前附加它:
// POST
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ModelClass1 ModelClass1)
{
if (ModelState.IsValid)
{
db.Attach(ModelClass1);
db.Entry(ModelClass1).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(ModelClass1);
}
但是我更愿意先从数据库中获取实体,然后更新属性:
// POST
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ModelClass1 ModelClass1)
{
if (ModelState.IsValid)
{
ModelClass1 entityFromDB = db.ModelClass1
.Include(i => i.ModelClass2.Select(c => c.ModelClass3))
.Where(x => x.Id == id)
.Single();
MapProperties(entityFromDB, ModelClass1); // You could use a mapper (Auto Mapper)
db.Entry(ModelClass1).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(ModelClass1);
}