繁体   English   中英

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

[英]EF 4: How to properly update object in DbContext using MVC with repository pattern

我正在尝试使用DBContext的ChangeTracker对象实现AuditLog,我遇到了DbEntityEntry.OriginalValues被消灭并被DbEntityEntry.CurrentValues替换的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);
    }
}

这将调用具体类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);
}

这个问题(在我以前的SO帖子中引起了我的注意)是,当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报告变化的能力

不,它不会混淆任何东西。 更改跟踪器功能基于更改跟踪器在进行更改之前知道实体的事实。 但在您的情况下,更改跟踪器会被告知已应用更改的实体,并且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内部规则。 设置实体的状态将改变该单个实体的状态,但不改变其关系。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM