OrionGuard.Outbox.SqlServerBroker 7.0.0

dotnet add package OrionGuard.Outbox.SqlServerBroker --version 7.0.0
                    
NuGet\Install-Package OrionGuard.Outbox.SqlServerBroker -Version 7.0.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="OrionGuard.Outbox.SqlServerBroker" Version="7.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OrionGuard.Outbox.SqlServerBroker" Version="7.0.0" />
                    
Directory.Packages.props
<PackageReference Include="OrionGuard.Outbox.SqlServerBroker" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add OrionGuard.Outbox.SqlServerBroker --version 7.0.0
                    
#r "nuget: OrionGuard.Outbox.SqlServerBroker, 7.0.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.
#:package OrionGuard.Outbox.SqlServerBroker@7.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=OrionGuard.Outbox.SqlServerBroker&version=7.0.0
                    
Install as a Cake Addin
#tool nuget:?package=OrionGuard.Outbox.SqlServerBroker&version=7.0.0
                    
Install as a Cake Tool

OrionGuard.Outbox.SqlServerBroker

Turns the outbox dispatcher from a poller into a listener on SQL Server: a trigger sends a Service Broker message when rows are inserted, and the dispatcher wakes instead of waiting out its interval.

dotnet add package OrionGuard.Outbox.SqlServerBroker
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Moongazing.OrionGuard.EntityFrameworkCore;
using Moongazing.OrionGuard.Outbox.SqlServerBroker;

public sealed class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options);

public static class WakeSignalSetup
{
    public static void Add(IServiceCollection services, string connectionString)
    {
        services.AddSqlServerBrokerOutboxWakeSignal(o => o.ConnectionString = connectionString);
        services.AddOrionGuardEfCore<AppDbContext>(opts => opts.UseOutbox());
    }
}

After the one-time setup below, an event committed by one process is dispatched by another within milliseconds instead of up to PollingInterval. OrionGuard.EntityFrameworkCore and Microsoft.Data.SqlClient come along as dependencies.

One-time setup

Service Broker has to be on for the database. ALTER DATABASE cannot run inside a transaction, and WITH ROLLBACK IMMEDIATE rolls back other sessions' open transactions — so this is a maintenance-window statement, not a migration step:

ALTER DATABASE [app] SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE;

Skip this statement on Azure SQL Managed Instance: Service Broker is on by default for every new database there and cannot be turned off, and both ENABLE_BROKER and DISABLE_BROKER are unsupported ALTER DATABASE options.

Then create the broker objects and the trigger, for example from a migration:

using Microsoft.EntityFrameworkCore.Migrations;
using Moongazing.OrionGuard.Outbox.SqlServerBroker;

public partial class InstallOrionGuardOutboxBroker : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder) =>
        migrationBuilder.Sql(SqlServerBrokerSetupSql.Create());

    protected override void Down(MigrationBuilder migrationBuilder) =>
        migrationBuilder.Sql(SqlServerBrokerSetupSql.Drop());
}

Create(tableName = "OrionGuard_Outbox", queueName = "OrionGuardOutboxQueue", serviceName = "OrionGuardOutboxService", contractName = "OrionGuardOutboxContract", messageTypeName = "OrionGuardOutboxRowInserted") creates the message type, contract, queue and service when they are missing, plus an AFTER INSERT trigger named orionguard_outbox_broker_notify that opens a dialog from the service to itself, sends one message and ends the conversation. Drop(...) takes the same parameters and removes everything. For custom names, pass them to both methods and set SqlServerBrokerOptions.QueueName to the same queue.

Every name must be a plain identifier: 1 to 128 ASCII letters, digits or underscores, not starting with a digit. Anything else — a schema-qualified schema.table included — throws ArgumentException, because the names are spliced into DDL and into the string EXEC runs, where a stray quote would otherwise break out of the literal.

How the wake-up works

AddSqlServerBrokerOutboxWakeSignal registers SqlServerBrokerOutboxWakeSignal as the IOutboxWakeSignal, replacing the polling-only default whichever order you call it in, and as a hosted service.

  • The hosted service opens a SqlConnection of its own — not one from your DbContext pool, because the connection sits in WAITFOR — and loops on WAITFOR (RECEIVE TOP(1) conversation_handle FROM [queue]), TIMEOUT <ReceiveTimeout>, ending each received conversation and waking the dispatcher. A WAITFOR that times out with no message wakes nothing.
  • Every SaveChanges/SaveChangesAsync in the same process also signals directly after the save. Wake-ups coalesce: at most one is pending at a time.
  • If the connection drops, the listener reconnects with a doubling delay. Until it is back the dispatcher falls back to OutboxOptions.PollingInterval, which bounds latency in every case.

Options

SqlServerBrokerOptions Default Notes
ConnectionString none Required.
QueueName OrionGuardOutboxQueue Must match the queue the setup SQL created.
ServiceName OrionGuardOutboxService Used by the setup SQL; the listener does not read it.
ReceiveTimeout 30 s How long one WAITFOR (RECEIVE ...) blocks. Must be > 0.
InitialReconnectDelay 1 s First delay after a connection failure; doubles each further failure. Must be > 0.
MaxReconnectDelay 30 s Upper bound on that delay. Must be at least InitialReconnectDelay.

A missing connection string or an invalid value makes the hosted service throw InvalidOperationException at host start, so the host does not start.

What this does not do

  • It is an optimization, not a delivery mechanism. A wake-up carries no rows: the dispatcher still takes its lock and reads the table, so across replicas only the lock holder dispatches. Correctness rests on the outbox table, which is why PollingInterval still bounds the worst case.
  • It needs Service Broker, which Azure SQL Database does not have. Microsoft's feature comparison lists Service Broker as No for Azure SQL Database and Yes for Azure SQL Managed Instance; a full SQL Server has it too. On Azure SQL Database this package cannot be used at all — leave the dispatcher polling, which is a supported configuration and not a fallback hack.
  • One outbox table per database. The trigger name is fixed and only created when missing, so a second outbox table cannot get its own trigger, and running Create again does not update an existing one — drop it first if you change the setup.
  • AFTER INSERT only. A row updated back into the unprocessed state, such as a dashboard replay, fires nothing and waits for the next poll.
  • No schema qualification. The table is resolved in the default schema of the user that runs the SQL.
  • It raises Microsoft.Data.SqlClient for your whole application, including the one Microsoft.EntityFrameworkCore.SqlServer uses. Check that against your other SqlClient consumers before adding it.
  • Shutdown is best effort. Hosted services stop in reverse registration order, so the listener may stop before the dispatcher; the dispatcher's remaining waits then simply last the polling interval.

Targets

net8.0, net9.0, net10.0; Microsoft.Data.SqlClient 7.x. Use the OrionGuard.EntityFrameworkCore build from the same OrionGuard release.

With the rest of OrionGuard

OrionGuard.EntityFrameworkCore (the outbox) · OrionGuard.Outbox.PostgresNotify (the PostgreSQL equivalent) · OrionGuard.Outbox.Dashboard · OrionGuard.Locks.Redis

Documentation

License

MIT. See LICENSE.txt.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
7.0.0 85 9/20/2026
6.7.0 134 7/20/2026
6.6.2 139 6/20/2026
6.6.1 126 6/20/2026
6.6.0 125 6/19/2026
6.5.30 135 6/17/2026
6.5.29 119 6/15/2026
6.5.28 122 6/15/2026
6.5.27 118 6/15/2026
6.5.26 120 6/13/2026
6.5.25 119 6/13/2026
6.5.24 123 6/12/2026
6.5.23 123 6/12/2026
6.5.22 120 6/11/2026
6.5.21 122 6/11/2026
6.5.20 117 6/11/2026
6.5.19 112 6/11/2026
6.5.18 113 6/11/2026
6.5.16 111 6/11/2026
6.5.15 117 6/11/2026
Loading failed