我一直在使用Entity Framework進行數據訪問的WinForms應用程序中使用MediatR庫的中介模式和CQRS進行試驗。該應用程序用於批量生產工廠,並允許用戶查看活動批次和完成批次的列表,並在必要時更新批次信息。每個批次都有大量與之相關的信息,例如質量和過程測量。讀取和寫入數據被組織到查詢和命令的基礎上,這些文章:MediatR和SimpleInjector的依賴範圍問題
Meanwhile... on the query side of my architecture
CQRS with MediatR and AutoMapper
下面是一個查詢和查詢處理的一個簡單的例子。使用SimpleInjector將DataContext
注入查詢處理程序。
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一個作用域或瞬時生活方式。然而,使用瞬時生活方式時,SimpleInjector引發以下錯誤,登記類型時如下被拋出:
container.Register<DataContext>();
類型「SimpleInjector.DiagnosticVerificationException」的未處理的異常發生在SimpleInjector.dll
附加信息:配置無效。報告了以下診斷警告:
- [一次性瞬態組件] DataContext被註冊爲瞬態,但實現了IDisposable。
研究的SimpleInjector網站在這個問題上顯示了以下note:
警告:瞬態情況下不會被容器跟蹤。這意味着Simple Injector不會處理瞬態實例。
這使我失望使用DataContext的一生範圍生活方式的路徑。要做到這一點,我創建了一個新的裝飾類爲我的查詢,如下注冊它:
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;
}
}
}
完美的作品!謝謝! –