Rebus.SqlServer 8.2.0

dotnet add package Rebus.SqlServer --version 8.2.0                
NuGet\Install-Package Rebus.SqlServer -Version 8.2.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Rebus.SqlServer" Version="8.2.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Rebus.SqlServer --version 8.2.0                
#r "nuget: Rebus.SqlServer, 8.2.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Rebus.SqlServer as a Cake Addin
#addin nuget:?package=Rebus.SqlServer&version=8.2.0

// Install Rebus.SqlServer as a Cake Tool
#tool nuget:?package=Rebus.SqlServer&version=8.2.0                

Rebus.SqlServer

install from nuget

Provides Microsoft SQL Server implementations for Rebus for

  • transport
  • sagas
  • subscriptions
  • timeouts
  • saga snapshots

alternate text is missing from this package README image


Which versions of SQL Server are supported?

Rebus' SQL package requires at least Microsoft SQL Server 2008.

A word of caution regarding the SQL transport

Microsoft SQL Server is a relational database and not a queueing system.

While it does provide the necessary mechanisms to implement queues, it's not optimized for the type of operations required to implement high-performance queues.

Therefore, please only use the SQL transport if your requirements are fairly modest (and what that means in practice probably depends a lot on the hardware available to you).

Configuration examples

The Rebus configuration spell goes either

services.AddRebus(configure => configure.(...));

or

Configure.With(...)
	.(...)
	.Start();

depending on whether you're using Microsoft DI or some other IoC container.

The following configuration examples will use the Microsoft DI-style of configuration, but the use of Rebus' configuration extensions is the same regardless of which type of configuration you are using, so it should be fairly easy to convert to the style you need.

Transport

Rebus only really requires one part of its configuration: A configuration of the "transport" (i.e. which queueing system, you're going to use).

The SQL transport is not recommended for heavier workloads, but it can be fine in cases where you do not require a super-high throughput. Here's how to configure it (in this case using the name queue-name as the name of the instance's input queue):

services.AddRebus(
	configure => configure
		.Transport(t => t.UseSqlServer(connectionString, "queue-name"))
);

Sagas

To configure Rebus to store sagas in SQL Server, you can do it like this (using the table 'Sagas' for the saga data, and 'SagaIndex' for the corresponding correlation properties):

services.AddRebus(
	configure => configure
		.(...)
		.Sagas(t => t.StoreInSqlServer(connectionString, "Sagas", "SagaIndex"))
);

Subscriptions

To configure Rebus to store subscriptions in SQL Server, you can do it like this (using the table 'Subscriptions'):

services.AddRebus(
	configure => configure
		.(...)
		.Subscriptions(t => t.StoreInSqlServer(connectionString, "Subscriptions", isCentralized: true))
);

Please note the use of isCentralized: true – it indicates that the subscription storage is "centralized", meaning that both subscribers and publishers use the same storage.

If you use the isCentralized: false option, then subscribers need to know the queue names of the publishers of the events they want to subscribe to, and then they will subscribe by sending a message to the publisher.

Using isCentralized: true makes the most sense in most scenarios, as it's easier to work with.

Timeouts

If you're using a transport that does not natively support "timeouts" (also known as "deferred messages", or "messages sent into the future" 🙂), you can configure one of your Rebus instances to function as a "timeout manager". The timeout manager must have some kind of timeout storage configured, and you can use SQL Server to do that.

You configure it like this (here using RabbitMQ as the transport):

services.AddRebus(
	configure => configure
		.Transport(t => t.UseRabbitMq(connectionString, "timeout_manager"))
		.Timeouts(t => t.StoreInSqlServer(connectionString, "Timeouts"))
);

In most cases, it can be super nice to simply configure one single timeout manager with a globally known queue name (e.g. "timeout_manager") and then make use of it from all other Rebus instances by configuring them to use the timeout manager for deferred messages:

services.AddRebus(
	configure => configure
		.Transport(t => t.UseRabbitMq(connectionString, "another-eueue"))
		.Timeouts(t => t.UseExternalTimeoutManager("timeout_manager"))
);

This will cause someMessage to be sent to the timeout manager when you await bus.Defer(TimeSpan.FromMinutes(5), someMessage), which will store it in its timeouts database for 5 minutes before sending it to whoever was configured as the recipient of someMessage.

Transactional Outbox

The transactional outbox in Rebus.SqlServer ensures consistent and reliable message delivery by storing outgoing messages in an outbox table within the same SQL transaction as your other database operations. This approach helps prevent data inconsistencies in case of failures, as it ties the message dispatch to the success of your data changes.

How to Configure the Transactional Outbox

Basic Setup

To configure the transactional outbox with Rebus, use the Outbox extension method during the setup. Rebus allows you to use any transport, and the outbox will work in conjunction with your chosen transport.

services.AddRebus(
    configure => configure
        .Transport(t => /* configure your transport here */)
        .Outbox(o => o.StoreInSqlServer(connectionString, "Outbox"))
);
  • connectionString: The connection string to your SQL Server database.
  • "Outbox": The name of the table where outbox messages will be stored.

Scenarios for Using the Transactional Outbox

Scenario 1: Outside a Rebus Handler

When you are outside a Rebus handler (e.g., in a web request or any other application context), you need to manage the SQL connection and transaction manually. Here's how you can do it:

using var connection = new SqlConnection(connectionString);
await connection.OpenAsync();
using var transaction = connection.BeginTransaction();

try
{
    using var scope = new RebusTransactionScope();
    scope.UseOutbox(connection, transaction);

    // Perform your database operations using 'connection' and 'transaction'

    // Send messages using Rebus
    await bus.Send(new YourMessage());

    // Complete the Rebus transaction scope
    await scope.CompleteAsync();

    // Commit your transaction
    await transaction.CommitAsync();
}
catch (Exception ex)
{
    // Handle exceptions
    await transaction.RollbackAsync();
    // Log or rethrow the exception as needed
}
  • Explanation:
    • You create and open a SqlConnection and begin a SqlTransaction.
    • Use scope.UseOutbox(connection, transaction) to inform Rebus to use your connection and transaction.
    • Perform your business logic and database operations within the transaction.
    • Send messages using Rebus; the messages will be stored in the outbox table within the same transaction.
    • After calling scope.CompleteAsync(), commit the transaction to ensure both your data changes and messages are persisted atomically.
Scenario 2: Inside a Rebus Handler

When inside a Rebus handler, Rebus manages the SQL connection and transaction for you. To include your database operations in the same transaction as Rebus, you can access the connection and transaction from the message context.

public class YourMessageHandler : IHandleMessages<YourMessage>
{
    public async Task Handle(YourMessage message)
    {
        var messageContext = MessageContext.Current 
            ?? throw new InvalidOperationException("No message context available.");

        var transactionContext = messageContext.TransactionContext;
        var outboxConnection = (OutboxConnection)transactionContext.Items["current-outbox-connection"];

        var connection = outboxConnection.SqlConnection;
        var transaction = outboxConnection.SqlTransaction;

        // Perform your database operations using 'connection' and 'transaction'

        // Send messages using Rebus; they will be included in the same transaction
        await messageContext.Bus.Send(new AnotherMessage());
    }
}
  • Explanation:
    • Retrieve the current MessageContext.
    • Access the OutboxConnection from the transaction context.
    • Use outboxConnection.SqlConnection and outboxConnection.SqlTransaction to perform your database operations.
    • Any messages you send will be stored in the outbox table within the same transaction.

What Happens After the Message is Stored

Once a message is stored in the outbox table and the transaction is committed, Rebus handles the retrieval and forwarding of the message to its intended destination. This ensures that message dispatch is reliable and decoupled from your application logic.

  • High-Level Overview:
    • The message remains in the outbox table until it is successfully dispatched.
    • Rebus periodically scans the outbox table for pending messages.
    • Upon successful delivery, messages are marked appropriately to prevent re-sending.
    • This mechanism ensures at-least-once delivery; your application should be designed to handle potential duplicate messages.

Example Projects

For practical examples of how to implement the transactional outbox with Rebus.SqlServer, you can refer to the following sample projects in the GitHub repository:

These examples demonstrate the outbox implementation in different contexts, including integration with Entity Framework Core.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (10)

Showing the top 5 NuGet packages that depend on Rebus.SqlServer:

Package Downloads
Ark.Tools.Rebus

NLog configuration helper and extensions for Ark standard configuration using code and not config files.

EventSub

Event Grid/Eventbridge style service that will store messages and forward to a subscriber URL

SuperiorAcumaticaPackage

Dependencies required to compile the SuperiorAcumaticaSolution for Acumatica 2024 R2 Build 24.204.0004

RAES.Core

Package Description

Bk.Rebus.EventBus

Common Rebus Configurations

GitHub repositories (4)

Showing the top 4 popular GitHub repositories that depend on Rebus.SqlServer:

Repository Stars
CodeMazeBlog/CodeMazeGuides
The main repository for all the Code Maze guides
mastreeno/Merp
An event based Micro ERP
DataDog/dd-trace-dotnet
.NET Client Library for Datadog APM
rebus-org/RebusSamples
Small sample projects
Version Downloads Last updated
8.2.0 388 12/16/2024
8.1.2 462,823 4/11/2024
8.1.1 176,821 2/7/2024
8.1.0 374 2/7/2024
8.0.3 17,602 2/7/2024
8.0.2 118,358 11/21/2023
8.0.1 1,354 11/21/2023
8.0.0 6,132 11/15/2023
8.0.0-alpha02 633 4/6/2023
7.3.1 271,006 2/6/2023
7.3.0 46,868 1/27/2023
7.3.0-b5 2,719 10/28/2022
7.3.0-b4 600 8/2/2022
7.3.0-b3 1,442 5/6/2022
7.3.0-b2 525 5/6/2022
7.3.0-b1 526 5/5/2022
7.2.0 769,550 10/23/2021
7.2.0-b2 3,167 3/24/2021
7.2.0-b1 632 3/2/2021
7.1.7 859,409 11/22/2020
7.1.6 4,191 11/16/2020
7.1.5 30,701 10/5/2020
7.1.4 7,547 9/27/2020
7.1.3 1,077 9/27/2020
7.1.2 4,614 9/17/2020
7.1.1 951 9/17/2020
7.1.0 2,121 9/13/2020
7.0.1 24,973 8/11/2020
7.0.0 32,547 8/3/2020
6.1.2 366,591 4/15/2020
6.1.1 5,947 4/10/2020
6.1.0 16,301 3/24/2020
6.0.1 8,910 3/3/2020
6.0.0 35,457 2/5/2020
6.0.0-a9 1,017 9/24/2019
6.0.0-a8 795 9/12/2019
6.0.0-a7 757 9/10/2019
6.0.0-a6 809 9/7/2019
6.0.0-a5 809 9/4/2019
6.0.0-a4 1,169 9/1/2019
6.0.0-a3 821 8/27/2019
6.0.0-a2 806 8/9/2019
6.0.0-a1 872 4/12/2019
5.1.0 1,164,240 4/12/2019
5.0.5 265,797 2/26/2019
5.0.3 275,781 11/11/2018
5.0.2 1,810 10/29/2018
5.0.1 2,637 10/11/2018
5.0.0 1,548 10/10/2018
5.0.0-b9 1,129 9/28/2018
5.0.0-b8 1,251 8/1/2018
5.0.0-b7 1,147 8/1/2018
5.0.0-b6 1,164 7/31/2018
5.0.0-b5 1,441 5/28/2018
5.0.0-b4 3,984 11/17/2017
5.0.0-b3 1,145 11/17/2017
5.0.0-b2 1,280 9/11/2017
5.0.0-b1 1,549 9/1/2017
4.0.0 417,390 8/15/2017
4.0.0-b08 1,294 7/18/2017
4.0.0-b07 1,314 6/12/2017
4.0.0-b06 1,417 5/18/2017
4.0.0-b03 1,217 3/21/2017
4.0.0-b02 1,203 3/21/2017
4.0.0-b01 1,269 3/21/2017
3.1.1 49,306 6/12/2017
3.1.0 12,477 3/1/2017
3.0.0 17,526 1/5/2017
2.1.0-b02 1,245 11/14/2016
2.1.0-b01 1,135 11/13/2016
2.0.0 20,780 9/20/2016
2.0.0-b04 1,237 9/14/2016
2.0.0-b03 1,179 9/12/2016
2.0.0-b02 1,101 9/12/2016
2.0.0-b01 1,186 9/9/2016