完整插入/更新/删除entity framework中的子实体

我知道它之前已被问过,但经过长时间的搜索和编码后,我无法采用干净利落的方法。 这是我有的:

public class QuestionModel { public int QuestionID { get; set; } public string QuestionText { get; set; } public IList Choices { get; set; } } public class QuestionChoiceModel { public int ChoiceID { get; set; } public string ChoiceText { get; set; } } 

我在这个ASP.Net MVC应用程序中使用EF 5。 使用InRequestScope() Ninject的通用存储库模式和dependency injection已经到位并且运行顺利。 这些模型映射到实体/从实体映射没有问题。

向数据库添加新问题很简单。 我设置了一些QuestionChoice实例的Question属性,EF处理其余的。

问题是关于更新。 假设我们在包含3个QuestionChoices的数据库中有一个问题:

 ChoiceID QuestionID ChoiceText -------- ---------- ---------- 1 1 blah blah 2 1 blah blah 3 1 blah blah 

当问题的编辑页面打开(GET:/ Questions / Edit / 1)时,我使用Razor中的foreach显示这3个选项。 我已经写了一些JQuery代码,如果用户愿意,可以为输入元素添加或删除所需的标记。 因此,可以在客户端上编辑ID = 1的QuestionChoice,可以删除ID = 2,并且可以添加新的ID = 4。 当用户按下Save按钮(POST:/ Questions / Edit / 1)时,表单数据将完美地绑定到QuestionModel。 模型正确映射到Question实体。 这就是故事开始的地方!

现在,Question实体有一个QuestionChoices集合,其中一些已经在数据库中,一些应该添加到数据库中,一些应该从数据库中删除。

我读过很多post: Entity Framework没有保存修改过的孩子

我可以用那种肮脏的方式处理编辑。 还有新记录:

 this._context.Entry(choice).State = EntityState.Added; 

但我正在寻找一种更优雅的方式。 并处理应删除的记录。 在这种情况下使用EF处理子实体的完整插入/更新/删除是否有好的方法? 老实说,我对EF的期望更高。

这是一个棘手的问题。 不幸的是,我无法提供您喜欢的解决方案。 我不相信这是可能的。 EF无法跟踪对您的实体所做的更改,除非它们是在检索实体的上下文中进行的 – 这在Web环境中是不可能的。 唯一可行的方法是在POST到/ Questions / Edit / 1之后检索Question对象(在上下文中),并在POSTed Question和从中检索的问题之间执行一种“合并”。数据库。 这将包括使用您的POSTed QuestionModel在您的QuestionModel上分配属性以及从数据库中检索的每个QuestionModel 。 我会说这也不是很好的做法,因为你会忘记包含一个属性。 它会发生。

我可以提供的最好(也是最简单)的解决方案是使用上面的.Entry()方法添加/编辑QuestionModelQuestionChoiceModel(s) 。 您将在此处牺牲“最佳实践”,以获得不易出错的解决方案。

 QuestionModel questionFromDb; QuestionModel questionFromPost; QuestionModelChoice[] deletedChoices = questionFromDb.Choices.Where(c => !questionFromPost.Choices.Any(c2 => c2.Id == c.Id)); using (var db = new DbContext()) { db.Entry(questionFromPost).State = questionFromPost.Id == 0 ? EntityState.Added : EntityState.Modified; foreach(var choice in questionFromPost.Choices) { db.Entry(choice).State = choice.Id == 0 ? EntityState.Added : EntityState.Modified; } foreach(var deletedChoice in deletedChoices) { db.Entry(deletedChoice).State = EntityState.Deleted; } db.SaveChanges(); } 

这只是概念的certificate。

控制器有函数UpdateModel但它不适用于包含子记录的更复杂的模型。 寻找TestUpdate

规则#1:每个表都有PK Id列。

规则#2:必须设置每个FK。

规则#3:需要设置级联删除。 如果你想删除相关记录。

规则#4:新记录需要Id = 0或更好才能为Null但Id不能为空。

 public class TestController : Controller where T : class { const string PK = "Id"; protected Models.Entities con; protected System.Data.Entity.DbSet model; public TestController() { con = new Models.Entities(); model = con.Set(); } // GET: Default public virtual ActionResult Index() { ViewBag.Result = TempData["Result"]; TempData["Result"] = null; var list = model.ToList(); return View(list); } [HttpGet] public virtual ActionResult AddEdit(string id) { int nId = 0; int.TryParse(id, out nId); var item = model.Find(nId); return View(item); } [HttpPost] public virtual ActionResult AddEdit(T item) { TestUpdate(item); con.SaveChanges(); return RedirectToAction("Index"); } [HttpGet] public virtual ActionResult Remove(string id) { int nId = 0; int.TryParse(id, out nId); if (nId != 0) { var item = model.Find(nId); con.Entry(item).State = System.Data.Entity.EntityState.Deleted; con.SaveChanges(); } return Redirect(Request.UrlReferrer.ToString()); } private void TestUpdate(object item) { var props = item.GetType().GetProperties(); foreach (var prop in props) { object value = prop.GetValue(item); if (prop.PropertyType.IsInterface && value != null) { foreach (var iItem in (System.Collections.IEnumerable)value) { TestUpdate(iItem); } } } int id = (int)item.GetType().GetProperty(PK).GetValue(item); if (id == 0) { con.Entry(item).State = System.Data.Entity.EntityState.Added; } else { con.Entry(item).State = System.Data.Entity.EntityState.Modified; } } } 

这是项目https://github.com/mertuarez/AspMVC_EF/

您需要为模型创建Controler并创建视图以进行操作。 如果是AddEdit操作,则必须为子类型创建编辑器模板。