EF 4:如何使用具有存储库模式的MVC正确更新DbContext中的对象

我正在尝试使用DBContext的ChangeTracker对象实现AuditLog,我遇到了DbEntityEntry.OriginalValues被消灭并被DbEntityEntry.CurrentValues替换的DbEntityEntry.CurrentValues 。 我注意到问题是我如何更新DbContext中正在跟踪的对象(原始post: entity frameworkDbContext SaveChanges()OriginalValue不正确 )。

所以现在我需要一些帮助,以正确的方式使用MVC 3中的存储库模式更新持久化对象与entity framework4.此示例代码改编自Apress推出的Pro Asp.NET MVC 3框架书中的SportsStore应用程序。

这是我在AdminController中的’编辑’post操作:

 [HttpPost] public ActionResult Edit(Product product) { if (ModelState.IsValid) { // Here is the line of code of interest... repository.SaveProduct(product, User.Identity.Name); TempData["message"] = string.Format("{0} has been saved", product.Name); return RedirectToAction("Index"); } else { // there is something wrong with the data values return View(product); } } 

这将调用具体类EFProductRepository(它实现IProductRepository接口并通过Ninject注入)。 以下是具体存储库类中的SaveProduct方法:

 public void SaveProduct(Product product, string userID) { if (product.ProductID == 0) { context.Products.Add(product); } else { context.Entry(product).State = EntityState.Modified; } context.SaveChanges(userID); } 

问题(在我之前的SOpost中引起了我的注意)是,当context.Entry(product).State = EntityState.Modified; 被称为它会以某种方式混淆ChangeTracker报告变化的能力。 所以在我重载的DBContext.SaveChanges(string userID)方法中,我没有在ChangeTracker.Entries().Where(p => p.State == System.Data.EntityState.Modified).OriginalValues看到准确的值ChangeTracker.Entries().Where(p => p.State == System.Data.EntityState.Modified).OriginalValues对象。

如果我将我的EFProductRepository.SaveProduct方法更新为此工作:

 public void SaveProduct(Product product, string userID) { if (product.ProductID == 0) { context.Products.Add(product); } else { Product prodToUpdate = context.Products .Where(p => p.ProductID == product.ProductID).FirstOrDefault(); if (prodToUpdate != null) { // Just updating one property to demonstrate.... prodToUpdate.Name = product.Name; } } context.SaveChanges(userID); } 

我想知道更新Product对象的正确方法,并在这种情况下将其保留,以便ChangeTracker准确地跟踪我对存储库中POCO类的更改。 我是否应该做后一个例子(当然除了复制可能已更新的所有字段),或者我应该采取不同的方法?

在这个例子中,“Product”类非常简单,只有字符串属性和十进制属性。 在我们的实际应用程序中,我们将具有“复杂”类型,POCO类将引用其他对象(即具有地址列表的Person)。 我知道我可能还需要做一些特别的事情来跟踪这种情况下的变化。 也许对此的了解将改变我在这里收到的一些建议。

它以某种方式弄乱了ChangeTracker报告变化的能力

不,它不会混淆任何东西。 更改跟踪器function基于更改跟踪器在进行更改之前知道实体的事实。 但在您的情况下,更改跟踪器会被告知已应用更改的实体,并且POCO实体不会保留有关其原始值的任何信息。 POCO实体只有一组值,它们被解释为当前值和原始值。 如果你想要别的什么,你必须自己编码。

我应该做后一个例子

在您的简单情况下是,您可以简单地使用:

 public void SaveProduct(Product product, string userID) { if (product.ProductID == 0) { context.Products.Add(product); } else { Product prodToUpdate = context.Products .Where(p => p.ProductID == product.ProductID).FirstOrDefault(); if (prodToUpdate != null) { context.Entry(prodToUpdate).CurrentValues.SetValues(product); } } context.SaveChanges(userID); } 

问题是这只适用于简单和复杂的属性。 另一个问题是,这会覆盖所有属性,因此,如果您的实体有一些您不想在UI中显示的字段(或者不想让用户编辑该字段),您仍必须为您的product设置正确的当前值实例,否则在应用当前值时将覆盖该值。

一旦您尝试将其应用于真实场景,整个情况就会变得非常复杂 。 您将失败并且在编写大量代码以准确支持您的案例之前会多次失败,因为可能没有通用解决方案EF没有针对此方案的支持方法。 原因是EF为每个实体和一些关联都有内部状态机,您必须为要更新,插入或删除的每个实体或关联配置状态,并且必须遵守EF内部规则。 设置实体的状态将改变该单个实体的状态,但不改变其关系。

我这样做只是通过从数据库加载当前实体与所有关系并手动(在代码中)合并整个实体图(只需要分离和附加实体图,你必须将所有更改从分离转移到附加的)。