【问题标题】:EF 4: How to properly update object in DbContext using MVC with repository patternEF 4:如何使用具有存储库模式的 MVC 正确更新 DbContext 中的对象
【发布时间】:2012-03-24 08:55:49
【问题描述】:

我正在尝试使用 DBContext 的 ChangeTracker 对象实现 AuditLog,我遇到了一个问题,DbEntityEntry.OriginalValues 被清除并替换为DbEntityEntry.CurrentValues。我注意到问题是如何我正在更新 DbContext 中正在跟踪的对象(原始帖子:Entity Framework DbContext SaveChanges() OriginalValue Incorrect)。

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

这是我在 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 对象中看不到准确的值。

如果我将我的 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 类将引用其他对象(即具有地址列表的人)。我知道我可能还需要做一些特别的事情来跟踪这种情况下的变化。也许了解这一点会改变我在这里收到的一些建议。

【问题讨论】:

    标签: c# asp.net-mvc-3 entity-framework


    【解决方案1】:

    它以某种方式破坏了 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 中显示的字段(或不想让用户编辑该字段),您仍然必须为您的 @ 设置正确的当前值987654323@ 实例,否则在应用当前值时该值将被覆盖。

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

    我只是通过从数据库中加载具有所有关系的当前实体并手动(在代码中)合并整个实体图来完成此操作(只是您已分离和附加实体图,您必须将所有更改从分离转移到附加一个)。

    【讨论】:

    • 感谢您的回复。你有你的方法的例子吗?我确实阅读了您链接到的 SO 问题,但我仍然不完全清楚。
    • Ladislav,我刚刚意识到我从来没有选择这个作为答案。你回答了我原来发布的问题,我很感激。此外,您对 EF 问题的 stackoverflow 的许多其他答案对我非常有帮助。再次感谢。
    猜你喜欢
    • 2011-09-04
    • 1970-01-01
    • 1970-01-01
    • 2011-07-13
    • 1970-01-01
    • 1970-01-01
    • 2012-02-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多