c#-MediatR和SimpleInjector的依赖项范围问题
作者:互联网
我一直在使用使用实体框架进行数据访问的WinForms应用程序中使用MediatR库尝试使用中介模式和CQRS.该应用程序用于批生产工厂,并允许用户查看有效和已完成批的列表,并在必要时更新批信息.每个批次都有大量与之相关的信息,例如质量和过程测量.基于以下文章,读写数据被组织为“查询和命令”:
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>();
An unhandled exception of type ‘SimpleInjector.DiagnosticVerificationException’ occurred in SimpleInjector.dll
Additional information: The configuration is invalid. The following diagnostic warnings were reported:
-[Disposable Transient Component] DataContext is registered as transient, but implements IDisposable.
在SimpleInjector网站上研究此问题后发现以下note:
Warning: Transient instances are not tracked by the container. This means that Simple Injector will not dispose transient instances.
这使我沿用了DataScope的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());
An unhandled exception of type ‘System.InvalidOperationException’ occurred in MediatR.dll
Additional information: Handler was not found for request of type MediatorTest.GetAllBatchesQuery.
Container or service locator not configured properly or handlers not registered with your container.
调试并查看MediatR代码后,似乎在调用mediator.Send(…)方法时,通过调用container.GetInstance()创建了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;
}
}
}
解决方法:
问题是您的装饰对象(具有DbContext依赖项)是在创建装饰器时创建的,并且那时没有活动范围(因为您是在以后的时间点创建的).您应该使用装饰性工厂(描述为here).换句话说,您的LifetimeScopeDecorator应该实现如下:
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>.这样,Simple Injector可以在创建范围之后将创建推迟.
标签:mediator,mediatr,c,entity-framework,simple-injector 来源: https://codeday.me/bug/20191118/2027583.html