NServiceBus 6中的早期消息调度问题。批处理消息分派不起作用?

NServiceBus 6中的早期消息调度问题。批处理消息分派不起作用?,nservicebus,Nservicebus,我们使用NServiceBus6传输MSMQ和RabbitMq。当我们使用带有分布式事务的MSMQ时,我们从未在分布式事务提交之前发送过消息。但是,当我们将其关闭并手动将其包装到事务范围内时,我们看到Nsb在处理程序执行结束之前开始发送消息。我们的代码如下所示 public Task Handle(ICommand1 message, IMessageHandlerContext context) { using (var tx = _session.BeginTransaction(Is

我们使用NServiceBus6传输MSMQ和RabbitMq。当我们使用带有分布式事务的MSMQ时,我们从未在分布式事务提交之前发送过消息。但是,当我们将其关闭并手动将其包装到事务范围内时,我们看到Nsb在处理程序执行结束之前开始发送消息。我们的代码如下所示

public Task Handle(ICommand1 message, IMessageHandlerContext context)
{
   using (var tx = _session.BeginTransaction(IsolationLevel.ReadCommitted))
   {    
       HandleMessage1(message1);

       _session.Flush();
       tx.Commit();
    }   
}

private void HandleMessage1(ICommand1 message1)
{
    // Updating database
    ...
    // Sending other Command2 to separate handler
    bus.Send<ICommand2>(x =>
    {
       ...
    });
}
公共任务句柄(ICommand1消息,IMessageHandlerContext)
{
使用(var tx=_session.BeginTransaction(IsolationLevel.ReadCommitted))
{    
HandleMessage1(message1);
_session.Flush();
tx.Commit();
}   
}
私有无效句柄消息1(ICommand1消息1)
{
//更新数据库
...
//将其他Command2发送到单独的处理程序
总线发送(x=>
{
...
});
}
从日志中,我可以看到ICommand2比ICommand1处理程序更早开始处理,ICommand1处理程序管理提交数据库中的数据更改,在Command1处理程序更新数据之前获取“旧”数据

我的印象是,我们不会有这种问题,因为Nsb6为我们提供了。看起来这不是我们的情况,我想知道为什么以及如何解决这个问题。我尝试在MSMQ(无distrib.transaction)和RabbitMq下运行它,结果是一样的


Command2处理程序处理Command1处理程序所做的更改,那么我们如何使它们按顺序工作?

从处理程序发送消息应该使用传递到处理程序的
上下文来完成。当您使用
bus.Send()
时,您可能使用的是
IMessageSession
,而不是
IMessageHandlerContext
,后者相当于立即分派

将代码更改为以下内容应可解决此问题:

公共异步任务句柄(ICommand1消息,IMessageHandlerContext)
{
使用(var tx=_session.BeginTransaction(IsolationLevel.ReadCommitted))
{    
等待HandleMessage1(message1,上下文);
_session.Flush();
tx.Commit();
}   
}
专用异步任务HandleMessage1(ICommand1 message1,IMessageHandlerContext)
{
//更新数据库
...
//将其他Command2发送到单独的处理程序
等待上下文。发送(x=>
{
...
});
}

从处理程序发送消息应该使用传递到处理程序的
上下文来完成。当您使用
bus.Send()
时,您可能使用的是
IMessageSession
,而不是
IMessageHandlerContext
,后者相当于立即分派

将代码更改为以下内容应可解决此问题:

公共异步任务句柄(ICommand1消息,IMessageHandlerContext)
{
使用(var tx=_session.BeginTransaction(IsolationLevel.ReadCommitted))
{    
等待HandleMessage1(message1,上下文);
_session.Flush();
tx.Commit();
}   
}
专用异步任务HandleMessage1(ICommand1 message1,IMessageHandlerContext)
{
//更新数据库
...
//将其他Command2发送到单独的处理程序
等待上下文。发送(x=>
{
...
});
}

这个“总线”变量实际上是包装了IMessageHandlerContext引用,所以最后它调用IMessageHandlerContext.IPipelineContext.Send,我们不使用ISendOnlyEndpointOk。确保它与
await
一起使用,就像我所展示的代码一样。在它内部实际运行context.Send(x=>…).GetAwaiter().GetResult()。它会导致问题吗?在什么内部?您不需要字段,可以在上下文中流动,也不需要在“总线”包装器中执行
GetAwaiter().GetResult()
。这个包装器的存在是为了覆盖不同的调用上下文,比如web或遗留代码,它们不在异步框架上,也不在Nsb处理程序中。我简化了示例代码,但HandleMessage1实际上是服务的方法,可以在不同的上下文中运行,而不仅仅是在Nsb处理程序中。这就是为什么我们把它包装在我们自己的“巴士”里,可以从任何地方出发。但是,对于这种特殊情况,它会选择我们存储在管道初始化上的IMessageHandlerContext。这个“bus”变量实际上是包装IMessageHandlerContext引用,所以最后它会调用IMessageHandlerContext.IPipelineContext.Send,我们不使用ISendOnlyEndpointOk。确保它与
await
一起使用,就像我所展示的代码一样。在它内部实际运行context.Send(x=>…).GetAwaiter().GetResult()。它会导致问题吗?在什么内部?您不需要字段,可以在上下文中流动,也不需要在“总线”包装器中执行
GetAwaiter().GetResult()
。这个包装器的存在是为了覆盖不同的调用上下文,比如web或遗留代码,它们不在异步框架上,也不在Nsb处理程序中。我简化了示例代码,但HandleMessage1实际上是服务的方法,可以在不同的上下文中运行,而不仅仅是在Nsb处理程序中。这就是为什么我们把它包装在我们自己的“巴士”里,可以从任何地方出发。然而,对于这个特殊的情况,它会选择我们存储在管道初始化上的IMessageHandlerContext。