返回旧数据的实体框架

本文关键字:实体 框架 数据 返回 | 更新日期: 2023-09-27 17:55:08

我遇到了 EF 在 3 层 WPF 应用程序中不返回最新数据的问题,我怀疑这与我如何处理上下文的生存期有关。这是场景:

有几个

存储库包装在一个工作单元中。还有一个服务(MyService),它使用UnitOfWork。还必须直接从 UI 调用此工作单元,而无需通过服务。

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

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

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

我正在使用 Unity 进行 DI,这是我的容器的样子:

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

为什么没有返回正确的数据,我该如何解决?

返回旧数据的实体框架

我终于发现了这个问题,这与我对 unitOfWork/dbcontext 生命周期的管理有关。

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

我找到了两种解决此问题的方法:

  1. 一个相当"黑客"的,强制实体重新加载:

    Context.Entry(entity).Reload();
    
  2. 使用 using 封装 unitOfWork 用法,以便在每个事务结束时释放上下文,从而在下次获取新数据。我认为这更符合UnitOfWork的用途,对我来说感觉更强大。我还将 UnitOfWork 包装在一个工厂中,所以现在它被注入到构造函数中。

    using (var uOw = new unitOfWorkFactory.GetNew())
    {
         // make the queries
    }   
    

Unity 的默认 LifetimeManager 是 TransientLifetimeManager,这意味着每次解析时(包括注入时)您都会获得一个新实例。 因此,根据您的注册,您将获得一个新的CarService,每次调用Resolve()时都会有一个新的UnitOfWork实例,并在主窗口ViewModel中注入一个新的,不同的实例。

因此,您的 ViewModel 获得一个 UoW,CarService 获得一个单独的 UoW,更新一个将意味着另一个由于缓存而过时。

您需要做的是为具有适当范围的上下文设置一个 LifetimeManager,或者遵从工厂。 Unity 没有内置那么多 LM,但 LifetimeManager 类基本上是一个美化的地图(本质上有一个 Set、Get 和 Remove 方法)。

我对 WPF 及其生命周期的了解不足以建议实现。 也许它可以是单例(它将在整个程序运行时保持相同的上下文),也许它可以由线程的 CallContext 支持。

您的另一个选择是在通过调用 Container.Resolve<CarService>(new ParameterOverride("unitOfWork", _unitOfWork)) 解析车载服务时传递 UoW 实例。 这将使生命周期管理与主窗口 ViewModel 的生命周期相关联。 但是,这种方法存在问题,因为您的 VM 类对 CarService 了解得太多(值得注意的是,它有一个 UoW)。