简单注入器,.NET Core 3.1,上下文对象的生命周期短

Simple Injector, .NET Core 3.1, short lifetime of a Context object

我正在尝试将我将通过控制器接收到的 correlationId 传递给我的 IEventDispatcher,我希望这个包含 correlationId 的对象在 EventHandler 搞定了。

此外,我正在收听 ServiceBus queuetopic,我也有 IEventDispatcher 的实例,我想应用相同的逻辑那里。

我在 Controller 中注入 EventDispatcher,我在其中设置了内部 Context,我希望可以在 EventHandler 中访问它,但不是通过直接传递 context 而是访问 context 通过 DI.

public class CommandController : ControllerBase
{
    private readonly IEventDispatcher eventDispatcher;

    public CommandController(IEventDispatcher eventDispatcher)
    {
        this.eventDispatcher = eventDispatcher;
    }

    [HttpPost]
    public async Task<IActionResult> Post([FromBody]TAction action)
    {
        try
        {
            // I want this context to be disposed after handler 
            // that is called inside of eventDispatcher is done with execution
            var context = new HttpRequestContext(HttpContext); 

            await eventDispatcher.Dispatch<TAction>((TAction)action, context);
            return Ok();
        } catch (Exception e)
        {
            return BadRequest((new BadExceptionResult { Error = e.Message }));
        }
    }
}    

我使用 dotnet core 3.1 web api,我的 SimpleInjector 设置如下:

class ServiceSetup    
    private Container container = new Container();

    public void ConfigureServices(IServiceCollection services)
    {
        container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
        // here I set RequestContextRegistrator 
        // and then I set this context inside EventHandler to set context 
        // and hope to have it available in handler
        container.Register<IRequestContextRegistrator, RequestContextRegistrator>(
            Lifestyle.Scoped);
        container.Register(() => container.GetInstance<IRequestContextRegistrator>().Get(),
            Lifestyle.Scoped);

        // ...
        // EventHandlers are registered just before EventDispatcher as EventDispatcher is
        // is depending on eventHandlers to be registered before eventDispatcher can send
        // a request to them
        var eventDispatcher = new EventDispatcher(container);

        services.AddSingleton<IEventDispatcher>(eventDispatcher);
        // ...
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseSimpleInjector(container);
        // ...
        container.Verify();
    }
}    

RequestContextRegistrator : IRequestContextRegistrator

internal class RequestContextRegistrator : IRequestContextRegistrator
{
    private readonly IContext context = new Context();

    public IContext RegisterContext(IContext context)
    {
        context.CorrelationId = new Guid().ToString();

        return context;
    }

    public Context Get()
    {
        return new Context()
        {
            CorrelationId = context.CorrelationId
        };
    }
}

这是EventDispatcher的样子

public class EventDispatcher : IEventDispatcher
{
    Container container;

    public EventDispatcher(Container container)
    {
        this.container = container;
    }

    public async Task Dispatch<TAction>(TAction action, IContext context)
    {
        using (AsyncScopedLifestyle.BeginScope(container))
        {
            // this is registered in `ConfigureServices` before  
            var handler = container.GetInstance(IEventHandler<TAction>);
        }
    }
}    

如您所见,我使用 using (AsyncScopedLifestyle.BeginScope(container)) 但在处理程序的构造函数中我从未注入 Context 对象,它始终是 null.

您可以将 IContext 存储在作用域开始处的作用域组件(例如您的 RequestContextRegistrator)中。例如:

public async Task Dispatch<TAction>(TAction action, IContext context)
{
    using (AsyncScopedLifestyle.BeginScope(container))
    {
        container.GetInstance<IContextProvider>().SetContext(context);

        var handler = container.GetInstance<IEventHandler<TAction>>();
        await handler.Handle(action);
    }
}

您的处理程序现在可以注入 IContextProvider 以访问 IContext:

public class OrderShippedHandler : IEventHandler<OrderShipped>
{
    private readonly IContextProvider provider;

    public OrderShippedHandler(IContextProvider provider) => this.provider = provider;

    public async Task Handle(OrderShipped e)
    {
        IContext context = this.provider.Context;
    }
}

不要忘记将 IContextProvider 实现注册为作用域。

这种在对象图中存储状态的方式称为 Closure Composition Model