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

Joe*_*ung 19 c# entity-framework asp.net-mvc-3

我正在尝试使用DBContext的ChangeTracker对象实现AuditLog,我遇到了一个问题,在这个问题上,它DbEntityEntry.OriginalValues被删除并被替换为DbEntityEntry.CurrentValues.我注意到问题是我如何更新DbContext中正在跟踪的对象(原始帖子:实体框架DbContext SaveChanges()OriginalValue不正确).

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

这是我在AdminController中的'编辑'帖子操作:

[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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这将调用具体类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);
}
Run Code Online (Sandbox Code Playgroud)

这个问题(在我之前的SO帖子中引起了我的注意)是,当context.Entry(product).State = EntityState.Modified;它被调用时,它会以某种方式弄乱ChangeTracker报告变化的能力.所以在我重载的DBContext.SaveChanges(字符串userID)方法中,我没有在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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

Lad*_*nka 35

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

不,它不会混淆任何东西.更改跟踪器功能基于更改跟踪器在进行更改之前知道实体的事实.但在您的情况下,更改跟踪器会被告知已应用更改的实体,并且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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 拉迪斯拉夫,我刚刚意识到我从来没有选择这个作为答案.你回答了我原来发布的问题,我很感激.此外,您对EF问题的stackoverflow上的许多其他答案对我来说非常有帮助.再次感谢. (5认同)