【问题标题】:Sending NServiceBus message inside TransactionScope在 TransactionScope 内发送 NServiceBus 消息
【发布时间】:2012-09-19 12:42:44
【问题描述】:

我正在尝试使用 NHibernate 在与从 MVC 应用程序内部在总线上发送消息相同的事务中保存到数据库:

public void DoSomethingToEntity(Guid id)
{
    var session = _sessionFactory.OpenSession();
    CurrentSessionContext.Bind(session);

    using (var transactionScope = new TransactionScope())
    {
        var myEntity = _session.Get(id);
        myEntity.DoSomething();
        _session.Save(myEntity);
        _bus.Send(myMessage);
        transactionScope.Complete();
    }

    session.Dispose();
}

在配置中,.MsmqTransport() 设置为 .IsTransactional(true)。

如果我在消息处理程序中执行此操作(它被包装在自己的事务中,因此不需要 TransactionScope)那么一切都会按预期工作,如果我包含异常,则两者都会失败。

但是,如果我在 MVC 应用程序中自己的事务中执行此操作,则在离开 using 块时,我会在 transactionScope.Complete() 之后收到以下错误。:

'该操作对于当前的登记状态无效。'

堆栈跟踪: 在 System.Transactions.EnlistmentState.InternalIndoubt(InternalEnlistment 登记) 在 System.Transactions.VolatileDemultiplexer.BroadcastInDoubt(VolatileEnlistmentSet& volatiles) 在 System.Transactions.TransactionStatePromotedIndoubt.EnterState(InternalTransaction tx) 在 System.Transactions.TransactionStatePromotedBase.InDoubtFromEnlistment(InternalTransaction tx) 在 System.Transactions.DurableEnlistmentDelegated.InDoubt(InternalEnlistment 登记,异常 e) 在 System.Transactions.SinglePhaseEnlistment.InDoubt(异常 e) 在 System.Data.SqlClient.SqlDelegatedTransaction.SinglePhaseCommit(SinglePhaseEnlistment 登记) 在 System.Transactions.TransactionStateDelegatedCommitting.EnterState(InternalTransaction tx) 在 System.Transactions.TransactionStateDelegated.BeginCommit(InternalTransaction tx,布尔 asyncCommit,AsyncCallback asyncCallback,对象 asyncState) 在 System.Transactions.CommittableTransaction.Commit() 在 System.Transactions.TransactionScope.InternalDispose() 在 System.Transactions.TransactionScope.Dispose() 在 C:\Users\paul.davies\Documents\GitHub\EdaCalendarExample\HumanResources.Application\Implementations\HolidayService.cs:line 76 中的 HumanResources.Application.Implementations.HolidayService.Book(BookHolidayRequest 请求) 在 C:\Users\paul.davies\Documents\GitHub\EdaCalendarExample\HumanResources.UI\Controllers\HolidayController.cs:line 82 中的 HumanResources.UI.Controllers.HolidayController.BookUpdate(BookHolidayViewModel viewModel) 在 lambda_method(闭包,ControllerBase,对象 []) 在 System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase 控制器,Object[] 参数) 在 System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext 控制器上下文,IDictionary2 parameters) at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 参数) 在 System.Web.Mvc.ControllerActionInvoker.c_DisplayClass15.b_12() 在 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter 过滤器,ActionExecutingContext preContext,Func`1 延续)

最新编辑:

此代码有效:

public void DoSomethingToEntity(Guid id)
{
    var session = _sessionFactory.OpenSession();
    CurrentSessionContext.Bind(session);

    using (var transactionScope = new TransactionScope())
    {
        var myEntity = _session.Get(id);
        _bus.Send(myMessage);
        transactionScope.Complete();
    }

    session.Dispose();
}

此代码会产生错误:

public void DoSomethingToEntity(Guid id)
{
    var session = _sessionFactory.OpenSession();
    CurrentSessionContext.Bind(session);

    using (var transactionScope = new TransactionScope())
    {
        var myEntity = _session.Get(id);
        myEntity.AnyField = "a new value";
        _bus.Send(myMessage);
        transactionScope.Complete();
    }

    session.Dispose();
}

请注意,在这两个示例中我都没有保存实体。不同之处在于第二个示例,我正在修改从 NHibernate 获得的实体。这是 100% 可重现的。

【问题讨论】:

  • 是否可以先从 Web 应用程序发送消息,然后让 NSB 进行数据库更新和任何其他后续 Sends()?
  • @AdamFyles 这可能并且会起作用,但会产生性能开销,我确信这是可以避免的。我知道使用 TransactionScope 是可能的,但我无法克服这个错误。
  • 致那些投反对票的人:我或其他 Stack Overflow 用户在不解释原因的情况下投反对票对我或其他 Stack Overflow 用户没有帮助。
  • 当 sql 驱动程序尝试将 TX 升级为分布式事务时,似乎 EX 发生了(因为 msmq 发送将强制升级)。它在处理程序上运行时起作用的原因是 NSB 已经从 msmq 接收到,因此当 sql 驱动程序登记时,TX 已经是分布式 TX。不知道为什么会发生这种情况
  • 这可能不相关,但即使会话刷新模式设置为 Commit,您仍然必须在提交 TransactionScope 之前调用 _session.Flush() - 这仅适用于 NH 提供的事务。

标签: asp.net-mvc messaging nservicebus transactionscope


【解决方案1】:

这可能不相关,但即使会话刷新模式设置为 Commit,您仍然必须在提交 TransactionScope 之前调用 _session.Flush() - 这仅适用于 NH 提供的事务。

【讨论】:

  • 是的,调用 session.Flush();在 transactionScope.Complete() 解决问题之前。我不完全理解为什么,但与在事务中同时包含 NServiceBus 和 NHibernate(或更具体地说,我认为是 ADO.NET)有关。
【解决方案2】:

据我所知,在创建新 System.Transactions.Transaction 时无法收到通知,并且查看 NHibernate 中的代码,它似乎没有任何代码来处理TransactionScope 是在创建会话之后创建的。

当您创建会话时,它会尝试加入当前事务,如果没有,则会话不会加入事务。我怀疑这是导致事务在提交时失败的原因。

我建议在 TransactionScope 内创建会话 - 还要检查您是否在 TransactionScope 之前的某处调用 session.BeginTransaction。

【讨论】:

  • 问题不是 NHibernate,而是 NServiceBus。据我所知,在创建会话后创建 TransactionScope 没有问题 - 它似乎确实按我预期的方式工作。无论如何我都尝试过,但结果相同。
  • 您确定要发送到的队列是事务性的吗?是本地队列还是远程队列?
  • 队列是事务性的,是的。
  • 如果每个操作都在自己的 TransactionScopes 中,它们是否成功完成?
  • Nhibernate 操作会,NService 总线操作不会。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-21
  • 2012-06-03
  • 2012-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多