Ребус Отправить в транзакцию

В предыдущих (‹=0.84.0) версиях Ребуса можно было отправить сообщение в TransactionScope и оно отправлялось только при заполнении области

using (var scope = new TransactionScope())
{
    var ctx = new AmbientTransactionContext();
    sender.Send(recipient.InputQueue, msg, ctx);

    scope.Complete();
}

Можно ли добиться такого же поведения в Rebus2


person Evgeniy Korniets    schedule 26.11.2015    source источник


Ответы (1)


Как вы правильно заметили, версия Rebus >= 0.90.0 не включается автоматически во внешние транзакции.

(ОБНОВЛЕНИЕ: начиная с 0.99.16 желаемое поведение может быть достигнуто - см. Конец этого ответа для получения подробной информации о том, как)

Однако это не означает, что Rebus не может участвовать в транзакции — он просто использует собственный механизм внешней транзакции (который не зависит от System.Transactions и будет доступен, когда Rebus будет портирован на ядро ​​.NET).

Вы можете использовать DefaultTransactionContext Ребуса и "сделать его эмбиентным" с этим AmbientRebusTransactionContext:

/// <summary>
/// Rebus ambient transaction scope helper
/// </summary>
public class AmbientRebusTransactionContext : IDisposable
{
    readonly DefaultTransactionContext _transactionContext = new DefaultTransactionContext();

    public AmbientRebusTransactionContext()
    {
        if (AmbientTransactionContext.Current != null)
        {
            throw new InvalidOperationException("Cannot start a Rebus transaction because one was already active!");
        }

        AmbientTransactionContext.Current = _transactionContext;
    }

    public Task Complete()
    {
        return _transactionContext.Complete();
    }

    public void Dispose()
    {
        AmbientTransactionContext.Current = null;
    }
}

который вы затем можете использовать следующим образом:

using(var tx = new AmbientRebusTransactionContext())
{
    await bus.Send(new Message());

    await tx.Complete();
}

или, если вы используете его в веб-приложении, я предлагаю вам обернуть его промежуточным программным обеспечением OWIN следующим образом:

app.Use(async (context, next) =>
{
    using (var transactionContext = new AmbientRebusTransactionContext())
    {
        await next();

        await transactionContext.Complete();
    }
});

ОБНОВЛЕНИЕ. Начиная с версии Rebus 0.99.16 поддерживается следующее (через пакет Rebus.TransactionScope ):

using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
    scope.EnlistRebus(); //< enlist Rebus in ambient .NET tx

    await _bus.SendLocal("hallå i stuen!1");

    scope.Complete();
}
person mookid8000    schedule 27.11.2015
comment
Этот подход подходит, если я хочу зарегистрироваться в Rebus TransactionContext. Но как насчет регистрации в .Net System.Transactions? Мне кажется, это обычная ситуация, когда у вас есть бизнес-логика, выполненная в .net Transactionscope, и вы хотите выполнить bus.Send как часть этой области. - person Evgeniy Korniets; 29.11.2015
comment
Я пошел дальше и реализовал то, что вы просили — теперь вы можете вызывать scope.EnlistRebus(); после создания области транзакции, чтобы задействовать внешнюю транзакцию Rebus в области — пожалуйста, помните об опции TransactionScopeAsyncFlowOption.Enabled! - person mookid8000; 30.11.2015
comment
В ожидании вашего ответа я реализовал свою собственную оболочку, которая работала как старомодная AmbientTransactionContext : IEnlistmentNotification, ITransactionContext, так и новая AmbientTransactionBridge. Но стандартная поддержка ОТЛИЧНАЯ. Вероятно, пришло время обновить github Wiki :) - person Evgeniy Korniets; 30.11.2015