Rebus.SqlServer
8.2.0
dotnet add package Rebus.SqlServer --version 8.2.0
NuGet\Install-Package Rebus.SqlServer -Version 8.2.0
<PackageReference Include="Rebus.SqlServer" Version="8.2.0" />
paket add Rebus.SqlServer --version 8.2.0
#r "nuget: Rebus.SqlServer, 8.2.0"
// 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
Provides Microsoft SQL Server implementations for Rebus for
- transport
- sagas
- subscriptions
- timeouts
- saga snapshots
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 aSqlTransaction
. - 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.
- You create and open a
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
andoutboxConnection.SqlTransaction
to perform your database operations. - Any messages you send will be stored in the outbox table within the same transaction.
- Retrieve the current
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 | Versions 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. |
-
.NETStandard 2.0
- microsoft.data.sqlclient (>= 5.2.2)
- rebus (>= 8.4.2)
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 |