16

EF が 3 層の WPF アプリケーションで最新のデータを返さないという問題があり、コンテキストの有効期間を処理する方法に関係があると思われます。これはシナリオです:

UnitOfWork 内にラップされたリポジトリがいくつかあります。UnitOfWork を使用する 1 つのサービス (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();

これが 2 回目に呼び出されたとき、値には最新バージョンのデータが含まれていません。ただし、DB では正常に更新されています。

私は 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 は DB からではなくキャッシュから値を取得していました。

これを修正する2つの方法を見つけました:

  1. かなり「ハック」なもので、エンティティを強制的にリロードします。

    Context.Entry(entity).Reload();
    
  2. unitOfWork の使用法を using でカプセル化して、コンテキストが各トランザクションの最後に破棄され、次回は新しいデータを取得できるようにします。これは UnitOfWork の目的に沿ったものであり、私にとってより堅牢に感じられると思います。また、UnitOfWork をファクトリにラップしたので、コンストラクタに挿入されます。

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

Unity のデフォルトの LifetimeManager は TransientLifetimeManager です。これは、解決するたびに新しいインスタンスを取得することを意味します (注入時を含む)。したがって、登録すると、Resolve() を呼び出すたびに UnitOfWork の新しいインスタンスを含む新しい CarService が取得され、メイン ウィンドウの ViewModel に新しい別のインスタンスが挿入されます。

したがって、ViewModel は UoW を取得し、CarService は別の UoW を取得します。1 つを更新すると、キャッシュのためにもう 1 つが古くなったことを意味します。

あなたがする必要があるのは、適切なスコープを持つコンテキストの LifetimeManager を設定するか、ファクトリに従うことです。Unity にはそれほど多くの LM が組み込まれていませんが、LifetimeManager クラスは基本的に美化されたマップです (本質的に Set、Get、および Remove メソッドがあります)。

WPF とその有効期間について、実装を提案するのに十分な知識がありません。シングルトン (プログラムが実行されている間ずっと同じコンテキストを維持する) かもしれませんし、スレッドの CallContext によってサポートされているかもしれません。

もう 1 つのオプションは、 を呼び出して CarService を解決するときに UoW インスタンスを渡すことContainer.Resolve<CarService>(new ParameterOverride("unitOfWork", _unitOfWork))です。これにより、ライフサイクル管理がメイン ウィンドウの ViewModel の有効期間に結び付けられます。ただし、このアプローチには問題があります。これは、VM クラスが CarService について (特に、UoW が含まれていることを) 認識しすぎているためです。

于 2013-01-22T00:32:14.223 に答える