我想通过 correlationId
我将通过控制器接收到我的 IEventDispatcher
我想让这个对象拥有 correlationId
尽快处理 EventHandler
是完成了它。
另外,我在听 ServiceBus
queue
和 topic
而我有实例 IEventDispatcher
我也想在那里应用同样的逻辑。
我注入了 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
内的Scoped组件(如您的 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
实现为作用域。
这种在对象图内部存储状态的方式称为 封口构成模型.