从命令处理程序中发布的事件

Event Published from within a Command Handler

使用 Rabbitmq 的 Rebus 的简单设置。 在 Consumer Window 服务的命令处理程序中,我正在发布一个事件(在命令处理程序的最开始,然后我正在进行实际处理)。 事件的事件处理程序也在消费者 window 服务中。我注意到虽然事件在一开始就发布了,但是它的事件处理程序只有在命令成功处理后才会执行。

在某种程度上是有道理的,除非命令(父)成功,否则不应处理事件(子)。

然而,将命令视为工作流时,我需要在常规阶段(关于特定状态的完成)发布事件。 Rebus 中的建议模式是什么?

正如您正确观察到的那样,Rebus 收集从消息处理程序内部发送的出站消息,确保它们在您的处理程序完成执行后发送。

原因是,这几乎总是您想要的——因为否则事件可能会被发布并因此被其他服务处理,甚至在您自己的工作单元被正确提交之前 (*)。

但是,如果您真的想要,您可以 "escape" Rebus 的传出消息集合,方法是暂时删除当前事务上下文,如下所示:

var transactionContext = AmbientTransactionContext.Current;
AmbientTransactionContext.Current = null;
try
{
    // current transaction will never know....
    await bus.Publish(whee);
}
finally
{
    AmbientTransactionContext.Current = transactionContext;
}

如果您决定使用这种转义事务上下文的方法,我建议您将其包装在实现 IDisposable 的东西中,这样您的代码就可以如下所示:

using(new RebusTransactionContextDismantler())
{
    // current transaction will never know....
    await bus.Publish(whee);
}

(*) 你可以想象这样一种情况,一个事件假设 FinalPaymentReceived,但是当订阅者处理它时,数据库中的订单没有相应更新,因为 SQL 事务已经还没有完全投入。

您还可以想象 SQL 事务由于违反唯一键约束而无法提交,这可能是由对某些特定对象进行的并行工作引起的,在这种情况下 SQL 事务被回滚。如果事件在那个时间点已经发布(并且可能已处理!),那将是非常灾难性的。