2016-07-27 8 views
1

私は、データアクセスのためにEntity Frameworkを使用するWinFormsアプリケーションでMediatRライブラリを使用してメディエータパターンとCQRSを試してきました。このアプリケーションはバッチ製造プラントで使用され、ユーザーはアクティブなバッチと完了したバッチのリストを表示し、必要に応じてバッチ情報を更新することができます。各バッチには、品質やプロセスの測定値など、関連する大量の情報があります。データの読み書きは、これらの記事に基づいて、クエリとコマンドで構成されています。ここ MediatRとSimpleInjectorの依存関係スコープの問題

Meanwhile... on the query side of my architecture

CQRS with MediatR and AutoMapper

は、クエリとクエリハンドラの簡単な例です。 DataContextは、SimpleInjectorを使用してクエリハンドラに挿入されます。

public class GetAllBatchesQuery: IRequest<IEnumerable<Batch>> { } 

public class GetAllBatchesQueryHandler : 
    IRequestHandler<GetAllBatchesQuery, IEnumerable<Batch>> 
{ 
    private readonly DataContext _context; 

    public GetAllBatchesQueryHandler(DataContext context) 
    { 
     _context= context; 
    } 

    public IEnumerable<Batch> Handle(GetAllBatchesQueryrequest) 
    { 
     return _db.Batches.ToList(); 
    } 
} 

これは以下のように発表者から呼び出されます:

var batches = mediator.Send(new GetAllBatchesQuery()); 

私はに実行している問題は、DbContextの寿命です。 Aのための品質メトリクスのリストを取得するデータベース

  • からバッチのリストを取得

    • :理想的には、私はこのケースでのようなものが含まれることになる、孤立したトランザクションごとに単一のインスタンスを使用したいですこれは、DbContextのためのスコープまたは一時的なライフスタイルに向けて私につながるデータベース

    に複数のエンティティを更新含まれるバッチ更新バッチ(これらは異なるデータベースに格納され、ストアドプロシージャを介してアクセスされる)

  • 、 。過渡的なライフスタイルを使用している場合しかし、SimpleInjectorは次のようにタイプを登録するときにスローされる次のエラーが、発生します

    container.Register<DataContext>(); 
    

    型「SimpleInjector.DiagnosticVerificationException」の未処理の例外はSimpleInjector.dll

    で発生しました

    追加情報:設定が無効です。

    - [Disposable Transient Component] DataContextは一時的に登録されていますが、IDisposableを実装しています。 SimpleInjectorのウェブサイト上でこの問題を調査

    には、以下のnoteを明らかに:

    警告:一時インスタンスは、コンテナによって追跡されていません。つまり、Simple Injectorは一時インスタンスを処理しません。

    これは、DataContextのLifetime Scopeライフスタイルを使用する道を導きました。これを達成するために、私は私のクエリのための新たなデコレータのクラスを作成し、次のように登録:

    public class LifetimeScopeDecorator<TRequest, TResponse> : 
        IRequestHandler<TRequest, TResponse> 
        where TRequest : IRequest<TResponse> 
    { 
        private readonly IRequestHandler<TRequest, TResponse> _decorated; 
        private readonly Container _container; 
    
        public LifetimeScopeDecorator(
         IRequestHandler<TRequest, TResponse> decorated, 
         Container container) 
        { 
         _decorated = decorated; 
         _container = container; 
        } 
    
        public TResponse Handle(TRequest message) 
        { 
         using (_container.BeginLifetimeScope()) 
         { 
          var result = _decorated.Handle(message); 
          return result; 
         } 
        } 
    } 
    
    ... 
    
    container.RegisterDecorator(
        typeof(IRequestHandler<,>), 
        typeof(ExecutionContextScopeDecorator<,>)); 
    

    ただし、その変更を行うことは別の例外が発生し、この時間は次の行で投げ:

    var batches = mediator.Send(new GetAllBatchesQuery()); 
    

    「System.InvalidOperationException」種類の未処理の例外はMediatR.dll

    で発生しました追加情報:ハンドラーは、タイプMediatorTest.GetAllBatchesQueryの要求が見つかりませんでした。

    コンテナまたはサービスロケータが正しく設定されていないか、コンテナにハンドラが登録されていません。デバッグ後

    とMediatRコードを見、mediator.Send(...)メソッドが呼び出されたときに、GetAllBatchesQueryHandlerクラスの新しいインスタンスを​​を呼び出すことによって作成されることが表示されます。ただし、DataContextはこの時点で実行スコープ内にないため、正しく初期化されず、例外が発生する可能性があります。

    私は問題の根本的な原因を理解していると思いますが、それを効果的に解決する方法については紛失しています。この問題をよりよく説明するために、以下の最小限の例を作成しました。 IDisposableを実装しているクラスであれば、私はDataContextと同じ問題が発生します。

    using System; 
    using System.Collections.Generic; 
    using System.Reflection; 
    using MediatR; 
    using SimpleInjector; 
    using SimpleInjector.Extensions.LifetimeScoping; 
    
    namespace MediatorTest 
    { 
        public class GetRandomQuery : IRequest<int> 
        { 
         public int Min { get; set; } 
         public int Max { get; set; } 
        } 
    
        public class GetRandomQueryHandler : IRequestHandler<GetRandomQuery, int> 
        { 
         private readonly RandomNumberGenerator _r; 
    
         public GetRandomQueryHandler(RandomNumberGenerator r) 
         { 
          _r = r; 
         } 
    
         public int Handle(GetRandomQuery request) 
         { 
          return _r.Next(request.Min, request.Max); 
         } 
        } 
    
        public class RandomNumberGenerator : IDisposable 
        { 
         private Random _random = new Random(); 
    
         public RandomNumberGenerator() { } 
    
         public void Dispose() { } 
    
         public int Next(int min, int max) 
         { 
          var result = _random.Next(min, max); 
          return result; 
         } 
        } 
    
        public class LifetimeScopeDecorator<TRequest, TResponse> : 
         IRequestHandler<TRequest, TResponse> 
         where TRequest : IRequest<TResponse> 
        { 
         private readonly IRequestHandler<TRequest, TResponse> _decorated; 
         private readonly Container _container; 
    
         public LifetimeScopeDecorator(
          IRequestHandler<TRequest, TResponse> decorated, 
          Container container) 
         { 
          _decorated = decorated; 
          _container = container; 
         } 
    
         public TResponse Handle(TRequest message) 
         { 
          using (_container.BeginLifetimeScope()) 
          { 
           var result = _decorated.Handle(message); 
           return result; 
          } 
         } 
        } 
    
        class Program 
        { 
         static void Main(string[] args) 
         { 
          var assemblies = GetAssemblies(); 
    
          var container = new Container(); 
          container.Options.DefaultScopedLifestyle = new LifetimeScopeLifestyle(); 
          container.RegisterSingleton<IMediator, Mediator>(); 
          container.Register<RandomNumberGenerator>(Lifestyle.Scoped); 
          container.Register(typeof(IRequestHandler<,>), assemblies); 
          container.RegisterSingleton(new SingleInstanceFactory(container.GetInstance)); 
          container.RegisterSingleton(new MultiInstanceFactory(container.GetAllInstances)); 
          container.RegisterDecorator(
           typeof(IRequestHandler<,>), 
           typeof(LifetimeScopeDecorator<,>)); 
    
          container.Verify(); 
    
          var mediator = container.GetInstance<IMediator>(); 
    
          var value = mediator.Send(new GetRandomQuery() { Min = 1, Max = 100 }); 
    
          Console.WriteLine("Value = " + value); 
    
          Console.ReadKey(); 
         } 
    
         private static IEnumerable<Assembly> GetAssemblies() 
         { 
          yield return typeof(IMediator).GetTypeInfo().Assembly; 
          yield return typeof(GetRandomQuery).GetTypeInfo().Assembly; 
         } 
        } 
    } 
    
  • 答えて

    2

    問題は(そのDbContext依存して)あなたのdecorateeは(あなたが後の時点で、それを作成するため)デコレータの作成時に作成され、その時点でアクティブなスコープが存在しないことです。 hereと記載されている装飾品工場を使用する必要があります。元の実装と

    public class LifetimeScopeDecorator<TRequest, TResponse> : 
        IRequestHandler<TRequest, TResponse> 
        where TRequest : IRequest<TResponse> 
    { 
        private readonly Func<IRequestHandler<TRequest, TResponse>> _decorateeFactory; 
        private readonly Container _container; 
    
        public LifetimeScopeDecorator(
         Func<IRequestHandler<TRequest, TResponse>> decorateeFactory, 
         Container container) 
        { 
         _decorateeFactory = decorateeFactory; 
         _container = container; 
        } 
    
        public TResponse Handle(TRequest message) 
        { 
         using (_container.BeginLifetimeScope()) 
         { 
          var result = _decorateeFactory.Invoke().Handle(message); 
          return result; 
         } 
        } 
    } 
    

    差がFunc<IRequestHandler<TRequest, TResponse>>IRequestHandler<TRequest, TResponse>の代わりに注入されることで次のように言い換えると、あなたLifetimeScopeDecoratorを実現すべきです。これにより、シンプルインジェクタは、スコープが作成された後に作成を延期することができます。

    +1

    完璧に動作します!ありがとうございました! –