簡體   English   中英

MediatR和SimpleInjector的依賴范圍問題

[英]Dependency Scope Issues with MediatR and SimpleInjector

我一直在WinForms應用程序中使用MediatR庫試驗中介模式和CQRS,該應用程序使用實體框架進行數據訪問。 該應用程序用於批生產工廠,允許用戶查看活動和已完成批次的列表,並在必要時更新批次信息。 每批都有大量與之相關的信息,例如質量和過程測量。 根據這些文章,讀取和寫入數據被組織到查詢和命令中:

同時......在我的架構的查詢方面

使用MediatR和AutoMapper的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM