简体   繁体   English

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

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

I'm trying to implement an AuditLog using the DBContext's ChangeTracker object, I ran into an issue where the DbEntityEntry.OriginalValues were getting wiped out and replaced with the DbEntityEntry.CurrentValues . 我正在尝试使用DBContext的ChangeTracker对象实现AuditLog,我遇到了DbEntityEntry.OriginalValues被消灭并被DbEntityEntry.CurrentValues替换的DbEntityEntry.CurrentValues It was brought to my attention that the problem was how I was updating the object that was being tracked in the DbContext (original post: Entity Framework DbContext SaveChanges() OriginalValue Incorrect ). 我注意到问题是我如何更新DbContext中正在跟踪的对象(原始帖子: 实体框架DbContext SaveChanges()OriginalValue不正确 )。

So now I need some help on the proper way to update a persisted object using the repository pattern in MVC 3 with Entity Framework 4. This example code is adapted from the SportsStore application in the Pro Asp.NET MVC 3 Framework book put out by Apress. 所以现在我需要一些帮助,使用MVC 3中的存储库模式和实体框架4更新持久化对象的正确方法。这个示例代码改编自Apress推出的Pro Asp.NET MVC 3框架书中的SportsStore应用程序。

Here is my 'Edit' post action in the AdminController: 这是我在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);
    }
}

This calls into concrete class EFProductRepository (which is implementing the IProductRepository interface and injected via Ninject). 这将调用具体类EFProductRepository(它实现IProductRepository接口并通过Ninject注入)。 Here is the SaveProduct method in the concrete repository class: 这是具体存储库类中的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);
}

The problem (as was brought to my attention in my previous SO post), is that when context.Entry(product).State = EntityState.Modified; 这个问题(在我以前的SO帖子中引起了我的注意)是,当context.Entry(product).State = EntityState.Modified; is called it somehow messes up the ChangeTracker's ability to report on the changes. 被称为它会以某种方式混淆ChangeTracker报告变化的能力。 So in my overloaded DBContext.SaveChanges(string userID) method, I am not seeing accurate values in the ChangeTracker.Entries().Where(p => p.State == System.Data.EntityState.Modified).OriginalValues object. 所以在我重载的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对象。

If I update my EFProductRepository.SaveProduct method to this it works: 如果我将我的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);
}

I would like to know the proper way to update the Product object and persist it in this scenario in such a way that the ChangeTracker accurately tracks my changes to the POCO class in the repository. 我想知道更新Product对象的正确方法,并在这种情况下将其保留,以便ChangeTracker准确地跟踪我对存储库中POCO类的更改。 Am I supposed to do the latter example (except of course copying over all fields that may have been updated), or should I be taking a different approach? 我是否应该做后一个例子(当然除了复制可能已更新的所有字段),或者我应该采取不同的方法?

In this example the "Product" class is very simple and only has string properties and decimal properties. 在此示例中,“Product”类非常简单,只有字符串属性和十进制属性。 In our real application we will have "complex" types and the POCO classes will reference other objects (ie Person that has a list of addresses). 在我们的实际应用程序中,我们将有“复杂”类型,POCO类将引用其他对象(即具有地址列表的Person)。 I know I may also need to do something special to track the changes in this case. 我知道我可能还需要做一些特别的事情来跟踪这种情况下的变化。 Perhaps knowledge of this will change some advice that I receive here. 也许这方面的知识会改变我在这里收到的一些建议。

it somehow messes up the ChangeTracker's ability to report on the changes 它以某种方式弄乱了ChangeTracker报告变化的能力

No it doesn't messes anything. 不,它不会混淆任何东西。 Change tracker ability is based on the fact that change tracker knows the entity prior to making changes. 更改跟踪器功能基于更改跟踪器在进行更改之前知道实体的事实。 But in your case the change tracker is informed about the entity with changes already applied and POCO entity doesn't keep any information about its original values. 但在您的情况下,更改跟踪器会被告知已应用更改的实体,并且POCO实体不会保留有关其原始值的任何信息。 POCO entity has only single set of values which is interpreted as both current and original. POCO实体只有一组值,它们被解释为当前值和原始值。 If you want anything else you must code it yourselves. 如果你想要别的什么,你必须自己编码。

Am I supposed to do the latter example 我应该做后一个例子

In your simple case yes and you can simply use: 在您的简单情况下是的,您可以简单地使用:

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);
}

The problem is that this works only for simple and complex properties. 问题是这只适用于简单和复杂的属性。 Another problem is that this overwrites all properties so if for example your entity has some field you don't want to show in UI (or don't want to let user to edit the field) you must still set correct current value to your product instance otherwise that value will be overwritten when applying current values. 另一个问题是,这会覆盖所有属性,因此,例如,如果您的实体有一些您不希望在UI中显示的字段(或者不想让用户编辑该字段),您仍必须为您的product设置正确的当前值实例,否则在应用当前值时将覆盖该值。

The whole situation becomes significantly more complex once you try to apply this to the real scenario. 一旦您尝试将其应用于真实场景,整个情况就会变得非常复杂 You will fail and you will fail many times before you write a lot of code to support exactly your cases because there is probably no generic solution EF has no supporting methods for this scenarios. 您将失败并且在编写大量代码以准确支持您的案例之前会多次失败,因为可能没有通用解决方案EF没有针对此方案的支持方法。 The reason is that EF has internal state machine for every entity and some associations and you must configure the state for every single entity or association you want to update, insert or delete and you must do it in compliance with EF internal rules. 原因是EF为每个实体和一些关联都有内部状态机,您必须为要更新,插入或删除的每个实体或关联配置状态,并且必须遵守EF内部规则。 Setting state of the entity will change the state of that single entity but not its relations. 设置实体的状态将改变该单个实体的状态,但不改变其关系。

I do it simply by loading current entity with all relations from database and manually (in code) merging whole entity graph (simply you have detached and attached entity graph and you must transfer all changes from detached to attached one). 我只是通过从数据库加载当前实体与所有关系并手动(在代码中)合并整个实体图(只需要分离和附加实体图,并且必须将所有更改从分离转移到附加的实体图)。

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

相关问题 使用存储库模式和DbContext删除EF 4中的关系键 - Delete Relationship Key in EF 4 with repository pattern and DbContext 如何使用存储库模式、服务模式、UnitOfWork、ORM(EF、NHibernate 等)构建带有 ASP MVC 的项目? - How to structure projects with ASP MVC using Repository Pattern, Service Pattern, UnitOfWork, ORM (EF, NHibernate etc..)? MVC EF数据库存储库模式 - MVC EF database repository pattern 如何不使用EF(存储库模式)在.NET MVC3中重复数据访问规则? - How to not repeat data access rules in .NET MVC3 using EF (Repository pattern)? 如何使用ContextFactory和存储库为WebAPI正确注入DbContext? - How to properly inject a DbContext for WebAPI using a ContextFactory and a repository? 如何正确地对与Entity Framework / DbContext耦合的存储库模式方法进行单元测试? - How do I properly unit test a repository-pattern method, that has coupling to Entity Framework / DbContext? MVC 5 EF6工作单元和存储库模式 - MVC 5 EF6 unit of work and repository pattern 如何使用Autofac将动态DbContext对象注入存储库 - How to inject dynamic DbContext object into repository using Autofac 在MVC / EF6 / WebApi2应用程序中使用DbContext的正确模式 - Correct pattern for DbContext use in MVC/EF6/WebApi2 application 存储库模式 EF Core 更新方法 - Repository Pattern EF Core Update method
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM