데이터 액세스를 위해 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의 수명과 함께합니다.
- 은 품질 메트릭의 목록을 가져 오는 데이터베이스
- 에서 일괄 처리 목록을 가져 오는 : 이상적으로,이 경우 이는 같은 것들을 포함 할 것, 고립 거래 당 단일 인스턴스를 사용하고 싶습니다 배치
- 데이터베이스에 여러 개체를 업데이트 포함 할 수있는 배치를 업데이트
이것은 DbContext에 대한 스코프 또는 일시적인 생활 양식으로 나를 이끌 것입니다 (이 저장 프로 시저를 통해 다른 데이터베이스에 저장하고 액세스 할 수 있습니다) .
container.Register<DataContext>();
형 'SimpleInjector.DiagnosticVerificationException'처리되지 않은 예외 SimpleInjector.dll
에서 발생 과도 라이프를 사용하는 경우에는, SimpleInjector는 종류를 등록 할 때 다음과 같이 발생되는 다음 오류 제기추가 정보 : 구성이 유효하지 않습니다. 다음과 같은 진단 경고가보고되었습니다.
- [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
클래스의 새 인스턴스를 container.GetInstance()
를 호출하여 생성 된 것 같습니다. 그러나 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;
}
}
}
완벽하게 작동합니다! 고맙습니다! –