我一直在使用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;
}
}
}
完美的作品!谢谢! –