{"id":19383257,"url":"https://github.com/rebus-org/rebus.sqlserver","last_synced_at":"2025-04-06T07:12:48.071Z","repository":{"id":51540876,"uuid":"67779769","full_name":"rebus-org/Rebus.SqlServer","owner":"rebus-org","description":":bus: Microsoft SQL Server transport and persistence for Rebus","archived":false,"fork":false,"pushed_at":"2025-03-21T09:44:44.000Z","size":4490,"stargazers_count":46,"open_issues_count":9,"forks_count":46,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-30T06:11:08.957Z","etag":null,"topics":["message-queue","persistence","rebus","sql-server","transport"],"latest_commit_sha":null,"homepage":"https://mookid.dk/category/rebus","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rebus-org.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-09-09T08:10:29.000Z","updated_at":"2025-03-21T09:44:36.000Z","dependencies_parsed_at":"2023-12-22T09:45:31.369Z","dependency_job_id":"598f42cf-437d-4669-a567-269307079ac5","html_url":"https://github.com/rebus-org/Rebus.SqlServer","commit_stats":null,"previous_names":[],"tags_count":78,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rebus-org%2FRebus.SqlServer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rebus-org%2FRebus.SqlServer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rebus-org%2FRebus.SqlServer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rebus-org%2FRebus.SqlServer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rebus-org","download_url":"https://codeload.github.com/rebus-org/Rebus.SqlServer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247445671,"owners_count":20939958,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["message-queue","persistence","rebus","sql-server","transport"],"created_at":"2024-11-10T09:25:14.514Z","updated_at":"2025-04-06T07:12:48.051Z","avatar_url":"https://github.com/rebus-org.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Rebus.SqlServer\n\n[![install from nuget](https://img.shields.io/nuget/v/Rebus.SqlServer.svg?style=flat-square)](https://www.nuget.org/packages/Rebus.SqlServer)\n\nProvides Microsoft SQL Server implementations for [Rebus](https://github.com/rebus-org/Rebus) for\n\n* transport\n* sagas\n* subscriptions\n* timeouts\n* saga snapshots\n\n![](https://raw.githubusercontent.com/rebus-org/Rebus/master/artwork/little_rebusbus2_copy-200x200.png)\n\n---\n\n\n# Which versions of SQL Server are supported?\n\nRebus' SQL package requires at least Microsoft SQL Server 2008.\n\n\n# A word of caution regarding the SQL transport\n\nMicrosoft SQL Server is a relational database and not a queueing system. \n\nWhile it does provide the necessary mechanisms to implement queues, it's not optimized for the type of operations required to implement high-performance queues.\n\nTherefore, 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).\n\n\n# Configuration examples\n\nThe Rebus configuration spell goes either \n```csharp\nservices.AddRebus(configure =\u003e configure.(...));\n```\nor\n```csharp\nConfigure.With(...)\n\t.(...)\n\t.Start();\n```\ndepending on whether you're using Microsoft DI or some other IoC container.\n\nThe 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\nof configuration you are using, so it should be fairly easy to convert to the style you need.\n\n\n## Transport\n\nRebus only really requires one part of its configuration: A configuration of the \"transport\" (i.e. which queueing system, you're going to use). \n\nThe 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\n(in this case using the name `queue-name` as the name of the instance's input queue):\n\n```csharp\nservices.AddRebus(\n\tconfigure =\u003e configure\n\t\t.Transport(t =\u003e t.UseSqlServer(connectionString, \"queue-name\"))\n);\n```\n\n\n## Sagas\n\nTo 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):\n\n```csharp\nservices.AddRebus(\n\tconfigure =\u003e configure\n\t\t.(...)\n\t\t.Sagas(t =\u003e t.StoreInSqlServer(connectionString, \"Sagas\", \"SagaIndex\"))\n);\n```\n\n\n## Subscriptions\n\nTo configure Rebus to store subscriptions in SQL Server, you can do it like this (using the table 'Subscriptions'):\n\n```csharp\nservices.AddRebus(\n\tconfigure =\u003e configure\n\t\t.(...)\n\t\t.Subscriptions(t =\u003e t.StoreInSqlServer(connectionString, \"Subscriptions\", isCentralized: true))\n);\n```\n\nPlease note the use of `isCentralized: true` – it indicates that the subscription storage is \"centralized\", meaning that both subscribers and publishers use the same storage.\n\nIf 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.\n\nUsing `isCentralized: true` makes the most sense in most scenarios, as it's easier to work with.\n\n\n## Timeouts\n\nIf 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\nas a \"timeout manager\". The timeout manager must have some kind of timeout storage configured, and you can use SQL Server to do that.\n\nYou configure it like this (here using RabbitMQ as the transport):\n\n```csharp\nservices.AddRebus(\n\tconfigure =\u003e configure\n\t\t.Transport(t =\u003e t.UseRabbitMq(connectionString, \"timeout_manager\"))\n\t\t.Timeouts(t =\u003e t.StoreInSqlServer(connectionString, \"Timeouts\"))\n);\n```\n\nIn 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\nconfiguring them to use the timeout manager for deferred messages:\n\n```csharp\nservices.AddRebus(\n\tconfigure =\u003e configure\n\t\t.Transport(t =\u003e t.UseRabbitMq(connectionString, \"another-eueue\"))\n\t\t.Timeouts(t =\u003e t.UseExternalTimeoutManager(\"timeout_manager\"))\n);\n```\n\nThis 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`.\n\n# Transactional Outbox\n\nThe 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.\n\n## How to Configure the Transactional Outbox\n\n### Basic Setup\n\nTo 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.\n\n```csharp\nservices.AddRebus(\n    configure =\u003e configure\n        .Transport(t =\u003e /* configure your transport here */)\n        .Outbox(o =\u003e o.StoreInSqlServer(connectionString, \"Outbox\"))\n);\n```\n\n- **`connectionString`**: The connection string to your SQL Server database.\n- **`\"Outbox\"`**: The name of the table where outbox messages will be stored.\n\n### Scenarios for Using the Transactional Outbox\n\n#### Scenario 1: Outside a Rebus Handler\n\nWhen 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:\n\n```csharp\nusing var connection = new SqlConnection(connectionString);\nawait connection.OpenAsync();\nusing var transaction = connection.BeginTransaction();\n\ntry\n{\n    using var scope = new RebusTransactionScope();\n    scope.UseOutbox(connection, transaction);\n\n    // Perform your database operations using 'connection' and 'transaction'\n\n    // Send messages using Rebus\n    await bus.Send(new YourMessage());\n\n    // Complete the Rebus transaction scope\n    await scope.CompleteAsync();\n\n    // Commit your transaction\n    await transaction.CommitAsync();\n}\ncatch (Exception ex)\n{\n    // Handle exceptions\n    await transaction.RollbackAsync();\n    // Log or rethrow the exception as needed\n}\n```\n\n- **Explanation**:\n  - You create and open a `SqlConnection` and begin a `SqlTransaction`.\n  - Use `scope.UseOutbox(connection, transaction)` to inform Rebus to use your connection and transaction.\n  - Perform your business logic and database operations within the transaction.\n  - Send messages using Rebus; the messages will be stored in the outbox table within the same transaction.\n  - After calling `scope.CompleteAsync()`, commit the transaction to ensure both your data changes and messages are persisted atomically.\n\n#### Scenario 2: Inside a Rebus Handler\n\nWhen 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.\n\n```csharp\npublic class YourMessageHandler : IHandleMessages\u003cYourMessage\u003e\n{\n    public async Task Handle(YourMessage message)\n    {\n        var messageContext = MessageContext.Current \n            ?? throw new InvalidOperationException(\"No message context available.\");\n\n        var transactionContext = messageContext.TransactionContext;\n        var outboxConnection = (OutboxConnection)transactionContext.Items[\"current-outbox-connection\"];\n\n        var connection = outboxConnection.SqlConnection;\n        var transaction = outboxConnection.SqlTransaction;\n\n        // Perform your database operations using 'connection' and 'transaction'\n\n        // Send messages using Rebus; they will be included in the same transaction\n        await messageContext.Bus.Send(new AnotherMessage());\n    }\n}\n```\n\n- **Explanation**:\n  - Retrieve the current `MessageContext`.\n  - Access the `OutboxConnection` from the transaction context.\n  - Use `outboxConnection.SqlConnection` and `outboxConnection.SqlTransaction` to perform your database operations.\n  - Any messages you send will be stored in the outbox table within the same transaction.\n\n## What Happens After the Message is Stored\n\nOnce 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.\n\n- **High-Level Overview**:\n  - The message remains in the outbox table until it is successfully dispatched.\n  - Rebus periodically scans the outbox table for pending messages.\n  - Upon successful delivery, messages are marked appropriately to prevent re-sending.\n  - This mechanism ensures **at-least-once** delivery; your application should be designed to handle potential duplicate messages.\n\n## Example Projects\n\nFor practical examples of how to implement the transactional outbox with Rebus.SqlServer, you can refer to the following sample projects in the GitHub repository:\n\n- [RebusOutboxWebApp](https://github.com/rebus-org/Rebus.SqlServer/tree/master/RebusOutboxWebApp)\n- [RebusOutboxWebAppEfCore](https://github.com/rebus-org/Rebus.SqlServer/tree/master/RebusOutboxWebAppEfCore)\n\nThese examples demonstrate the outbox implementation in different contexts, including integration with Entity Framework Core.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frebus-org%2Frebus.sqlserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frebus-org%2Frebus.sqlserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frebus-org%2Frebus.sqlserver/lists"}