16

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

UnitOfWork 中包含多个存储库。还有一项服务 (MyService),它使用 UnitOfWork。这个 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>();
     }
}

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

4

2 回答 2

25

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

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

我找到了两种解决方法:

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

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

    using (var uOw = new unitOfWorkFactory.GetNew())
    {
         // make the queries
    }   
    
于 2013-01-22T15:27:40.917 回答
0

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

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

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

我对 WPF 及其生命周期知之甚少,无法建议实施。也许它可以是单例的(它将在程序运行的整个过程中保持相同的上下文),也许它可以由线程的 CallContext 支持。

您的另一个选择是在通过调用解决 CarService 时传递 UoW 实例Container.Resolve<CarService>(new ParameterOverride("unitOfWork", _unitOfWork))。这将使生命周期管理与主窗口 ViewModel 的生命周期保持联系。但是,这种方法存在问题,因为您的 VM 类对 CarService 了解得太多(特别是其中包含 UoW)。

于 2013-01-22T00:32:14.223 回答