简单注入器,.NET Core 3.1,上下文对象的生命周期短
Simple Injector, .NET Core 3.1, short lifetime of a Context object
我正在尝试将我将通过控制器接收到的 correlationId
传递给我的 IEventDispatcher
,我希望这个包含 correlationId
的对象在 EventHandler
搞定了。
此外,我正在收听 ServiceBus
queue
和 topic
,我也有 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。
我正在尝试将我将通过控制器接收到的 correlationId
传递给我的 IEventDispatcher
,我希望这个包含 correlationId
的对象在 EventHandler
搞定了。
此外,我正在收听 ServiceBus
queue
和 topic
,我也有 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。