Repository EF模式中Update函数的意义是什么? [英] What is the point of the Update function in the Repository EF pattern?

查看:65
本文介绍了Repository EF模式中Update函数的意义是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在EF中使用网上找到的更新函数使用存储库模式

I am using the repository pattern within EF using an Update function I found online

public class Repository<T> : IRepository<T> where T : class
{
    public virtual void Update(T entity)
    {
        var entry = this.context.Entry(entity);
        this.dbset.Attach(entity);
        entry.State = System.Data.Entity.EntityState.Modified;
    }
}

然后我在 DeviceService 像这样:

public void UpdateDevice(Device device)
{
    this.serviceCollection.Update(device);
    this.uow.Save();
}

我已经意识到这实际上是在更新设备的所有信息不仅仅是更新已更改的属性。这意味着在多线程环境中更改可能会丢失。

I have realise that what this actually does it update ALL of the device's information rather than just update the property that changed. This means in a multi threaded environment changes can be lost.

经过测试,我意识到我可以更改 Device 然后调用 uow.Save()既保存了数据又没有覆盖任何现有更改。

After testing I realised I could just change the Device then call uow.Save() which both saved the data and didnt overwrite any existing changes.

所以我的问题是确实是- Update()函数的意义是什么?

So my question really is - What is the point in the Update() function? It appears in almost every Repository pattern I find online yet it seems destructive.

推荐答案

我不会称其为通用的 Update 方法通常是破坏性的,但我同意它的用例有限,在那些存储库实现中很少讨论。该方法是否有用取决于所要应用的方案。

I wouldn't call this generic Update method generally "destructive" but I agree that it has limited use cases that are rarely discussed in those repository implementations. If the method is useful or not depends on the scenario where you want to apply it.

在附加方案(例如Windows Forms应用程序)中,您加载实体从数据库中更改一些属性,而这些属性仍附加到EF上下文中,然后保存更改,该方法就没有用了,因为该上下文无论如何都会跟踪所有更改,并在最后知道必须更新或不更新哪些列。在这种情况下,您根本不需要Update方法(提示: DbSet< T> (这是通用存储库)没有为此,请更新方法)。在并发情况下,它是破坏性的。

In an "attached scenario" (Windows Forms application for instance) where you load entities from the database, change some properties while they are still attached to the EF context and then save the changes the method is useless because the context will track all changes anyway and know at the end which columns have to be updated or not. You don't need an Update method at all in this scenario (hint: DbSet<T> (which is a generic repository) does not have an Update method for this reason). And in a concurrency situation it is destructive, yes.

但是,不清楚变更跟踪更新有时是否也具有破坏性。如果两个用户将同一属性更改为不同的值,则这两个用户的更改跟踪更新将保存新的列值,最后一个获胜。是否可以,取决于应用程序以及希望更改的安全性。如果应用程序不允许在保存更改之前编辑不是数据库中最新版本的对象,则它不允许最后一次保存获胜。它必须停止,迫使用户重新加载最新版本,并在输入更改之前查看最后一个值。为了处理这种情况,并发令牌是必需的,它可以检测到其他人同时更改了记录。但是,这些并发检查与更改跟踪更新或将实体状态设置为已修改时的工作方式相同。并发异常阻止了这两种方法的破坏潜力。但是,将状态设置为 Modified 仍然会产生不必要的开销,因为它会将未更改的列值写入数据库。

However, it is not clear that a "change tracked update" isn't sometimes destructive either. If two users change the same property to different values the change tracked update for both users would save the new column value and the last one wins. If this is OK or not depends on the application and how secure it wants changes to be done. If the application disallows to ever edit an object that is not the last version in the database before the change is saved it cannot allow that the last save wins. It would have to stop, force the user to reload the latest version and take a look at the last values before he enters his changes. To handle this situation concurrency tokens are necessary that would detect that someone else changed the record in the meantime. But those concurrency checks work the same way with change tracked updates or when setting the entity state to Modified. The destructive potential of both methods is stopped by concurrency exceptions. However, setting the state to Modified still produces unnecessary overhead in that it writes unchanged column values to the database.

在分离方案(例如Web应用程序)中,更改跟踪更新不可用。如果您不想将整个实体设置为 Modified ,则必须从数据库中加载最新版本(在新的上下文中),请复制来自UI并再次保存更改。但是,这并不能防止其他用户在此期间所做的更改被覆盖,即使这些更改是对不同属性的更改也是如此。想象两个用户同时将同一个客户实体加载到Web表单中。用户1编辑客户名称并保存。用户2编辑客户的银行帐号,然后保存几秒钟。如果将实体加载到新上下文中以对用户2执行更新,那么EF只会看到数据库中的客户名称(已经包括用户1的更改)与用户2发送回的客户名称(后者仍然是旧客户名称)。如果您复制客户名称值,则该属性将被标记为已修改,而旧名称将被写入数据库并覆盖用户1的更改。此更新与将整个实体状态设置为已修改一样具有破坏性。为了避免出现此问题,您将不得不在客户端实施一些自定义更改跟踪,以识别用户2是否更改了客户名称,如果没有更改,只是不将该值复制到加载的实体。否则,您将不得不再次使用并发令牌。

In a "detached scenario" (Web application for example) the change tracked update is not available. If you don't want to set the whole entity to Modified you have to load the latest version from the database (in a new context), copy the properties that came from the UI and save the changes again. However, this doesn't prevent that changes another user has done in the meantime get overwritten, even if they are changes on different properties. Imagine two users load the same customer entity into a web form at the same time. User 1 edits the customer name and saves. User 2 edits the customer's bank account number and saves a few seconds later. If the entity gets loaded into the new context to perform the update for User 2 EF would just see that the customer name in the database (that already includes the change of User 1) is different from the customer name that User 2 sent back (which is still the old customer name). If you copy the customer name value the property will be marked as Modified and the old name will be written to the database and overwrite the change of User 1. This update would be just as destructive as setting the whole entity state to Modified. In order to avoid this problem you would have to either implement some custom change tracking on client side that recognizes if User 2 changed the customer name and if not it just doesn't copy the value to the loaded entity. Or you would have to work with concurrency tokens again.

您没有提到此 Update 的最大局限性您问题中的方法-即它不会更新任何相关实体。例如,如果您的设备实体具有相关的 Parts 集合,则可以在分离的UI中编辑该集合(添加/ remove / modify items)将父 Device 的状态设置为 Modified 不会将这些更改保存到数据库。它只会影响父 Device 本身的标量(复杂)属性。在使用这种类型的存储库时,我将更新方法命名为 FlatUpdate ,以在方法名称中更好地表明该限制。我从未见过通用的 DeepUpdate 。处理复杂的对象图始终是非通用的,必须根据实体类型针对每种实体类型分别编写。 (幸运的是,像 GraphDiff 可以限制必须为此类图形更新编写的代码量。)

You didn't mention the biggest limitation of this Update method in your question - namely that it doesn't update any related entities. For example, if your Device entity had a related Parts collection and you would edit this collection in a detached UI (add/remove/modify items) setting the state of the parent Device to Modified won't save any of those changes to the database. It will only affect the scalar (and complex) properties of the parent Device itself. At the time when I used repos of this kind I named the update method FlatUpdate to indicate that limitation better in the method name. I've never seen a generic "DeepUpdate". Dealing with complex object graphs is always a non-generic thing that has to be written individually per entity type and depending on the situation. (Fortunately a library like GraphDiff can limit the amount of code that has to be written for such graph updates.)

故事短:


  • 对于附加方案, Update 方法是多余的,因为EF会自动更改跟踪完成了将正确的UPDATE语句写入数据库的所有必要工作-包括相关对象图的更改。

  • 对于分离的场景,这是一种执行简单的没有关系的实体更新的简便方法。

  • 使用简化的 Update 方法无法完成在分离方案中使用父实体和子实体更新对象图的过程,并且需要大量操作更多(非通用)工作。

  • 安全并发控制需要更复杂的工具,例如enabli ng EF提供的乐观并发检查并以用户友好的方式处理所产生的并发异常。

  • For attached scenarios the Update method is redundant as EFs automatic change tracking does all the necessary work to write correct UPDATE statements to the database - including changes in related object graphs.
  • For detached scenarios it is a comfortable way to perform updates of simple entities without relationships.
  • Updating object graphs with parent and child entities in a detached scenario can't be done with such a simplified Update method and requires significantly more (non-generic) work.
  • Safe concurrency control needs more sophisticated tools, like enabling the optimistic concurrency checks that EF provides and handling the resulting concurrency exceptions in a user-friendly way.

这篇关于Repository EF模式中Update函数的意义是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆