[英]Correctly set Scope when using WebApi, SimpleInjector and MediatR
[英]Dependency Scope Issues with MediatR and SimpleInjector
我一直在WinForms應用程序中使用MediatR庫試驗中介模式和CQRS,該應用程序使用實體框架進行數據訪問。 該應用程序用於批生產工廠,允許用戶查看活動和已完成批次的列表,並在必要時更新批次信息。 每批都有大量與之相關的信息,例如質量和過程測量。 根據這些文章,讀取和寫入數據被組織到查詢和命令中:
這是一個查詢和查詢處理程序的簡單示例。 使用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.dll中發生未處理的“SimpleInjector.DiagnosticVerificationException”類型異常
附加信息:配置無效。 報告了以下診斷警告:
- [Disposable Transient Component] DataContext注冊為transient,但實現了IDisposable。
在SimpleInjector網站上研究此問題會顯示以下注釋 :
警告:容器不跟蹤瞬態實例。 這意味着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());
MediatR.dll中發生了未處理的“System.InvalidOperationException”類型異常
其他信息:找不到Handler,因為MediatorTest.GetAllBatchesQuery類型的請求。
未正確配置容器或服務定位器或未在容器中注冊的處理程序。
在調試並查看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;
}
}
}
問題是你的decoratee(帶有DbContext
依賴關系)是在創建裝飾器時創建的,並且那時沒有活動范圍(因為你在以后的某個時間點創建它)。 你應該使用這里描述的decoratee工廠。 換句話說,您的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在創建范圍后推遲創建。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.