问题描述
|
场景:
检索某些实体
更新这些实体的一些属性
您执行某种业务逻辑,该逻辑指示您不再应该更新那些属性。相反,您应该插入一些新实体来记录您的业务逻辑结果。
插入新实体
保存更改();
显然,在上面的示例中,调用SaveChanges()不仅会插入新实体,还会更新原始实体的属性。在设法以某种方式对上下文(及其实体)进行更改之前,我只能在确定要保存所有更改的情况下对代码(及其实体)进行更改,但是并非总是如此。所以问题是处理这种情况的最佳方法是什么?如果重要的话,我不会直接使用上下文,而是通过存储库。有没有简单的方法可以将实体还原为原始值?在这种情况下的最佳实践是什么?
更新资料
尽管我不同意Ladislav的看法,即应该以这样一种方式重新安排业务逻辑,即验证总是在对实体进行任何修改之前进行,但我同意解决方案实际上应该在不同的上下文中持久保留所需的更改。我不同意的原因是,我的业务交易时间很长,并且在交易结束时可能发生的验证或错误检查并不总是很明显。想象一下,您正在使用从上至下的灯光来装饰圣诞树,而当您在下部分支上工作时,您已经对圣诞树进行了修改。如果其中一盏灯坏了怎么办?您想回滚所有更改,但是要创建一些ERROR实体。正如Ladislav所建议的,最直接的方法是将ERROR实体保存在不同的上下文中,从而允许原始实体(带有经过修改的隐喻树)过期而无需调用SaveChanges。
现在,在我的情况下,我使用Ninject进行依赖关系注入,将一个EF上下文注入到我的所有顶级服务范围内的存储库中。这意味着我的业务层类实际上没有创建新的EF上下文的控制权。它们不仅不能访问EF上下文(请记住它们通过存储库工作),而且在对象层次结构中的注入已经发生了。我发现的唯一解决方案是创建另一个类,该类将利用Ninject在其中创建一个新的UOW。
//business logic executing against repositories with already injected and shared (unit of work) context
Tree = treeRepository.Get();
Lights = lightsRepsitory.Get();
//update the tree as you\'re decorating it with lights
if(errors.Count == 0)
{
//no errors,calling SaveChanges() on any one repository will commit the entire UOW as they all share the same injected EF context
repository1.SaveChanges();
}
else
{
//oops one of the lights broke,we need to insert some Error entities
//however if we just add id to the errorRepository and call SaveChanges() the modifications that happened
//to the tree will also be committed.
TreeDecoratorErroHandler.Handle(errors);
}
internal class TreeDecoratorErroHandler
{
//declare repositories
//constructor that takes repository instances
public static void Handle(IList<Error> errors)
{
//create a new Ninject kernel
using(Ninject... = new Ninject...)
{
//this will create an instance that will get injected with repositories sharing a new EF instance
//completely separate from the one outside of this class
TreeDecoratorErroHandler errorHandler = ninjectKernel.Get<TreeDecoratorErroHandler>();
//this will insert the errors and call SaveChanges(),the only changes in this new context are the errors
errorHandler.InsertErrors(errors);
}
}
//other methods
}
解决方法
您绝对应该为此使用新的上下文。上下文是工作单元,一旦您的业务逻辑说:“嘿,我不想更新此实体”,那么该实体就不是工作单元的一部分。您可以分离实体或创建新的上下文。
可以使用
Refresh
方法,但是应该在必须处理乐观并发的情况下使用该方法。因此,如果实体的一部分,此方法仅刷新标量和复杂属性以及外键-如果对导航属性进行了更改,则刷新实体后这些内容仍然存在。
, 看看带有RefreshMode.StoreWins的ObjectContext.Refresh,我认为这将满足您的要求。启动一个新的上下文将实现我猜想的相同结果,但不会那么整洁。