Skip to content
Mogens Heller Grabe edited this page Dec 28, 2022 · 1 revision

Rebus has an outbox implementation for Microsoft SQL Server.

The outbox is capable of storing outgoing messages in a table in SQL Server as an intermediate step, before actually sending them using whatever transport you have configured.

This is cool when you're already doing work in SQL Server, e.g. by mutating some entities with Entity Framework, or with some manually crafted INSERT/UPDATE/DELETE statements.

To make this work, it requires tight integration with your SQL transaction. To make this work with Rebus, you'll need to observe these two scenarios:

  1. When you're in a Rebus handler
  2. When you're NOT in a Rebus handler

Here's everything you need to know to configure the outbox to work in those two scenarios - first, you enable the outbox like so:

services.AddRebus(
    configure => configure
        .Transport(..)
        .Outbox(o => o.StoreInSqlServer(connectionString, "Outbox"))
);

When this is done, Rebus will automatically create the necessary table in SQL Server when it starts.

That was the first step - now for the two scenarios:

1. When you're in a Rebus handler

When you're in a Rebus handler, Rebus will manage the SQL transaction, and it will automatically store all outgoing sent/published messages in the outbox. Once your Rebus handler has finished executing without throwing an exception, Rebus will commit the transaction.

The thing YOU'LL need to do, is to do your own work using Rebus' SQL connection and SQL transaction. You can retrieve the connection and transaction like this:

var messageContext = MessageContext.Current 
                     ?? throw new InvalidOperationException("Cannot get the message context outside of a Rebus handler");

var transactionContext = messageContext.TransactionContext;

var outboxConnection = transactionContext.Items.TryGetValue("current-outbox-connection", out var result)
    ? (OutboxConnection) result
    : throw new KeyNotFoundException("Could not find OutboxConnection under the key 'current-outbox-connection'");

where the OutboxConnection object then holds the SqlConnection and SqlTransaction. You can then use those to carry out whatever SQL commands you need.

⚠️ Please note that Rebus will manage the connection and the transaction objects, so the following things will interfere with that and ruin everything:

  • close the connection
  • commit/roll back the transaction
  • dispose any of them

and probably more. So please don't do that. 🙂

2. When you're NOT in a Rebus handler

When you're NOT in a Rebus handler, YOU'LL need to manage the SQL connection and SQL transaction, and then you can make Rebus enlist all of its outbox SQL commands in the same transaction by doing this:

// you'll need to get an SqlConnection and an SqlTransaction from somewhere
using var connection = GetSqlConnection();
using var transaction = connection.BeginTransaction();

try
{
    // then you'll need a Rebus tx scope
    using var scope = new RebusTransactionScope();

    // and then enable the outbox for that scope
    scope.UseOutbox(connection, transaction);

    //         execute your code here 👇 
    // --> THIS is where your code will execute <--
    //                there     ☝️ 

    // completing the scope will insert outgoing messages using the connection/transaction
    await scope.CompleteAsync();

    // commit all the things! 👍 
    await transaction.CommitAsync();    
}
catch (Exception exception)
{
    // log it or something
}

which you will probably want to write into your application framework somehow. E.g. if you're in an ASP.NET Core web app, you'll most likely want to implement a piece of middleware that has all of the interesting bits from the snippet above in it.

Clone this wiki locally