私は、データアクセスのために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のためのスコープまたは一時的なライフスタイルに向けて私につながるデータベース
に複数のエンティティを更新含まれるバッチ更新バッチ(これらは異なるデータベースに格納され、ストアドプロシージャを介してアクセスされる)
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;
}
}
}
完璧に動作します!ありがとうございました! –