实体框架返回旧数据

17

我在一个三层WPF应用程序中使用EF时遇到了问题,它没有返回最新的数据,我怀疑与上下文的生命周期处理有关。具体情况如下:

有几个仓储被封装在一个UnitOfWork中。还有一个服务(MyService),它使用UnitOfWork。必须直接从UI调用此UnitOfWork,而不通过服务传递。

在我的主窗口的ViewModel中,我创建了一个新窗口(使用ViewModel first):

var dialog = new DialogViewModel(_eventAggregator, _unitOfWork, Container.Resolve<CarService>());

这个主窗口的ViewModel有一个UnitOfWork,在构造函数中已经注入,它被传递给了DialogViewModel。

CarService的构造函数也需要一个UnitOfWork,它也在它的构造函数中被注入:

public CarService(IUnitOfWork unitOfWork){
    _unitOfWork = unitOfWork;
}

在DialogViewModel中使用CarService进行查询以检索一些数据并进行一些更新时,第一次使用它是正常的。然而,当下一次使用相同的查询检索该数据时,它返回的是旧/缓存的数据而不是最新修改的数据。使用UnitOfWork(在CarService内部)进行的查询如下:

var values = _unitOfWork.GarageRepository.GetSomeValues();
_unitOfWork.GarageRepository.MakeSomeChangesToTheValuesUsingStoredProcedure();

第二次调用时,values不包含最新版本的数据;但它已经在数据库中成功更新。

我正在使用Unity进行依赖注入,这是我的容器的样子:

public class Container
{
     public static UnityContainer Container = new UnityContainer();

     // Called once in the AppBoostraper, as soon as the GUI application starts 
     public void BuildUp()
     {
          Container.RegisterType<IUnitOfWork, UnitOfWork>();
          Container.RegisterType<ICarService, CarService>();
     }
}

为什么返回的数据不正确,我该如何修复?


注意:数据更改是通过调用存储过程进行的,而不是修改上下文中的实体。 - chuwik
2个回答

29

我最终找到了问题所在,与我管理unitOfWork/dbcontext生命周期有关。

我加载了一些实体,然后使用存储过程更新它们(因此代码中的实体不再是最新的),然后重新加载查询;此时EF从缓存中获取值而不是从数据库中获取值。

我找到了两种解决方法:

  1. 一种相当“hacky”的方式,强制实体重新加载:

Context.Entry(entity).Reload();
使用using封装unitOfWork的使用,以便在每个事务结束时处理上下文并因此在下一次获取新数据。我认为这更符合UnitOfWork的预期,并且对我来说感觉更加强大。我还将UnitOfWork包装在工厂中,因此现在在构造函数中注入它。
using (var uOw = new unitOfWorkFactory.GetNew())
{
     // make the queries
}   

0
Unity 的默认 LifetimeManager 是 TransientLifetimeManager,这意味着每次解析时(包括注入时),都会得到一个新的实例。因此,根据您的注册,每次调用 Resolve() 都会得到一个新的 CarService,并且每个实例都有一个新的 UnitOfWork 实例,同时注入到主窗口 ViewModel 中的是另一个不同的实例。
因此,您的 ViewModel 获取了一个 UoW,CarService 获取了一个单独的 UoW,更新一个将意味着由于缓存而使另一个现在过时。
您需要做的是为具有适当范围的上下文设置合适的 LifetimeManager,或者退回到工厂模式。 Unity 并没有太多内置的 LM,但 LifetimeManager 类基本上是一个映射表(具有 Set、Get 和 Remove 方法)。
我不了解 WPF 及其生命周期的足够信息来建议实现。也许它可以是 singleton(它将在程序运行期间始终保持相同的上下文),也许它可以由线程的 CallContext 支持。

你的另一个选择是在解析CarService时传递UoW实例,方法是调用Container.Resolve<CarService>(new ParameterOverride("unitOfWork", _unitOfWork))。这将使生命周期管理与主窗口ViewModel的生命周期绑定。然而,这种方法存在问题,因为你的VM类对CarService了解得有点太多(特别是它包含一个UoW)。


感谢您的回答。然而,我混淆了DialogViewModel构造函数,并已更新问题,更改并澄清其他部分(当DialogViewModel调用CarService方法读取/更新数据时,两个查询都会运行,因此我猜测它们都使用相同的实例)。我的应用程序是在WPF中完成的,因此其中没有涉及ASP。 - chuwik
哎呀,你说得对。基本问题仍然存在:你同时拥有两个不同的UoW实例:一个在CarService中,另一个在ViewModel中。编辑描述了一些可能的解决方案。 - Nick
我已经尝试了你的选项,将UnitOfWork的lifetimemanager更改为单例,甚至修改了代码直接传递相同的_unitOfWork,只是为了看看它是否起作用;然而在每个实例中仍然返回旧数据。 - chuwik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接