我创建了一个复杂的模型,并且一直在努力寻找一种方法来将表单数据添加/更新到数据库。
基本上我的模型如下所示:
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控制器,并尝试使用一个Edit类更新所有3个实体。
我的编辑(获取/发布)课程:
// 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个模型类中的每一个创建模板,类似于下面的模板:
@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>
数据正确显示然后我正在调用编辑控制器但是当我修改表单中的数据并发布它调用HTTPPost Edit方法时,我收到以下错误:
附加“ModelClass3”类型的实体失败,因为同一类型的另一个实体已经具有相同的主键值。如果图中的任何实体具有冲突的键值,则在使用“附加”方法或将实体的状态设置为“未更改”或“已修改”时,可能会发生这种情况。这可能是因为某些实体是新的并且尚未收到数据库生成的键值。在这种情况下,使用“添加”方法或“已添加”实体状态来跟踪图形,然后根据需要将非新实体的状态设置为“未更改”或“已修改”。
如果我从Controller / View中完全删除ModelClass3,则不会收到任何错误,但修改后的数据不会保存到数据库中。
我很确定我的方法不是最好的方法,所以任何帮助都会受到高度赞赏,因为我的想法已经不多了。
答案 0 :(得分:1)
首先,我不建议您直接在前端使用您的域名实体。我会为您的域实体创建一个视图模型(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);
}