我使用我在网上找到的Update
函数在EF中使用存储库模式
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();
}
我已经意识到这实际上会更新所有设备的信息,而不仅仅是更新已更改的属性。这意味着在多线程环境中,更改可能会丢失。
经过测试后,我意识到我可以更改Device
,然后调用uow.Save()
,这两者都保存了数据并且没有覆盖任何现有的更改。
所以我的问题是 - Update()
功能有什么意义?它几乎出现在我在网上找到的每个Repository模式中,但似乎具有破坏性。
答案 0 :(得分:2)
我不会将此通用Update
方法称为&#34; destructive&#34;但我同意它有限的用例很少在那些存储库实现中讨论。如果方法有用或取决于您要应用它的方案。
在附加场景&#34; (例如,Windows Forms应用程序)从数据库加载实体,在它们仍附加到EF上下文时更改某些属性,然后保存更改方法无用,因为上下文无论如何都会跟踪所有更改并在最后知道哪些列必须更新或不更新。在这种情况下,您根本不需要Update方法(提示:DbSet<T>
(这是一个通用存储库)因此没有Update
方法。在并发情况下它是破坏性的,是的。
然而,目前尚不清楚&#34;更改跟踪更新&#34;也不具备破坏性。如果两个用户将同一属性更改为不同的值,则两个用户的更改跟踪更新将保存新列值,最后一个获胜。如果这是好的,取决于应用程序以及它希望进行更改的安全性。如果应用程序不允许在保存更改之前编辑不是数据库中最后一个版本的对象,则不能允许上次保存获胜。它必须停止,强制用户重新加载最新版本并在输入更改之前查看最后的值。要处理这种情况,并发令牌是必要的,可以检测到其他人在此期间更改了记录。但是,这些并发检查在更改跟踪更新或将实体状态设置为Modified
时的工作方式相同。并发异常会阻止这两种方法的破坏性潜力。但是,将状态设置为Modified
仍会产生不必要的开销,因为它会将未更改的列值写入数据库。
在&#34;分离的场景&#34; (例如Web应用程序)更改跟踪更新不可用。如果您不想将整个实体设置为Modified
,则必须从数据库中加载最新版本(在新上下文中),复制来自UI的属性并再次保存更改。但是,这并不能防止另一个用户在此期间所做的更改被覆盖,即使它们是对不同属性的更改。想象一下,两个用户同时将同一个客户实体加载到Web表单中。用户1编辑客户名称并保存。用户2编辑客户的银行帐号并在几秒钟后保存。如果实体加载到新上下文中以执行用户2的更新,EF只会看到数据库中的客户名称(已包含用户1的更改)与用户2发回的客户名称不同(其中仍旧是旧客户名称)。如果复制客户名称值,则该属性将被标记为已修改,旧的名称将写入数据库并覆盖用户1的更改。此更新与将整个实体状态设置为已修改一样具有破坏性。为了避免这个问题,您必须在客户端实现一些自定义更改跟踪,以识别用户2是否更改了客户名称,如果不是,则不会将值复制到加载的实体。或者你必须再次使用并发令牌。
您没有在提问中提及此Update
方法的最大限制 - 即它不会更新任何相关实体。例如,如果您的Device
实体具有相关的Parts
集合,并且您将在分离的UI(添加/删除/修改项目)中编辑此集合,则将父Device
的状态设置为Modified
无法将任何更改保存到数据库中。它只会影响父Device
本身的标量(和复杂)属性。当我使用这种repos时,我命名了更新方法FlatUpdate
以在方法名称中更好地指示该限制。我从未见过通用&#34; DeepUpdate
&#34;。处理复杂的对象图总是一种非通用的东西,必须根据实际类型单独编写,具体取决于具体情况。 (幸运的是,像GraphDiff这样的库可以限制为这种图形更新而必须编写的代码量。)
简而言之:
Update
方法是多余的,因为EFs自动更改跟踪会执行将正确的UPDATE语句写入数据库所需的所有工作 - 包括相关对象图中的更改。Update
方法完成,并且需要更多(非泛型)工作。答案 1 :(得分:1)
在Slauma非常深刻而实用的答案之后,我想放大一些基本原则。
在this MSDN article中有一个重要的句子
存储库将业务逻辑与基础数据源或Web服务的交互分开。
简单的问题。与Update
有关的业务逻辑是什么?
Fowler将存储库模式定义为
使用类似集合的接口访问域对象,在域和数据映射层之间进行调解。
因此,就业务逻辑而言,存储库只是一个集合。集合语义是关于添加和删除对象,或检查对象是否存在。主要操作是Add
,Remove
和Contains
。查看ICollection<T>
界面:那里没有Update
方法。
对象是否应标记为“已修改”,这不是业务逻辑的关注点。它只是修改对象并依赖其他层来检测和持久化更改。公开Update
方法
if
构造都会进入,以检查值是否有变化。UPDATE
。