Transport Configuration¶
Verified by tests
ServiceCollectionExtensionsBranchCoverageTests, ServiceCollectionExtensionsTests, RoutingBuilderExtensionsTests — library CI run #31657041675 (2026-08-13)
This guide covers configuring message transports in Whizbang, including environment-based switching between RabbitMQ (local development) and Azure Service Bus (production).
Transport Options¶
| Transport | Package | Use Case |
|---|---|---|
| RabbitMQ | SoftwareExtravaganza.Whizbang.Transports.RabbitMQ |
Local development, Aspire |
| Azure Service Bus | SoftwareExtravaganza.Whizbang.Transports.AzureServiceBus |
Production, Azure deployment |
| None (local dispatch) | Built-in | Unit testing (no transport registration needed) |
Environment-Based Transport Switching¶
Recommended Pattern: Runtime Configuration¶
Recommended Pattern: Runtime Configuration
var builder = WebApplication.CreateBuilder(args);
// Core Whizbang setup (storage; connection string resolved from configuration)
builder.Services
.AddWhizbang()
.WithEFCore<AppDbContext>("postgres")
.WithDriver.Postgres;
// Transport switching based on configuration
var useRabbitMQ = builder.Configuration.GetValue<bool>("UseRabbitMQ");
if (useRabbitMQ) {
// Local development with Aspire/RabbitMQ
builder.Services.AddRabbitMQTransport(
builder.Configuration.GetConnectionString("rabbitmq")!,
options => {
options.MaxChannels = 10;
options.PrefetchCount = 200;
});
builder.Services.AddRabbitMQHealthChecks();
} else {
// Production with Azure Service Bus
builder.Services.AddAzureServiceBusTransport(
builder.Configuration.GetConnectionString("servicebus")!,
options => {
options.MaxConcurrentCalls = 16;
options.DefaultSubscriptionName = "order-service";
});
builder.Services.AddAzureServiceBusHealthChecks();
}
Configuration Files¶
appsettings.Development.json:
Configuration Files
{
"UseRabbitMQ": true,
"ConnectionStrings": {
"postgres": "Host=localhost;Database=myapp;Username=postgres;Password=postgres",
"rabbitmq": "amqp://guest:guest@localhost:5672"
}
}
appsettings.Production.json:
Configuration Files (2)
{
"UseRabbitMQ": false,
"ConnectionStrings": {
"postgres": "Host=myapp.postgres.database.azure.com;Database=myapp;...",
"servicebus": "Endpoint=sb://myapp.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=..."
}
}
Note: Transport options (
RabbitMQOptions,AzureServiceBusOptions) are configured through the callback passed to theAdd*Transportregistration, not bound from configuration sections. Topics, exchanges, and subscriptions are auto-provisioned from your routing configuration (see Message Routing below) — there is no manual exchange/topic naming option on the transport itself.
RabbitMQ Configuration¶
Basic Setup¶
Basic Setup
Advanced Configuration¶
Advanced Configuration
builder.Services.AddRabbitMQTransport(
builder.Configuration.GetConnectionString("rabbitmq")!,
options => {
// Channel pooling
options.MaxChannels = 10;
// Consumer configuration
options.PrefetchCount = 200;
options.EnableSingleActiveConsumer = false;
// Delivery / dead-lettering
options.MaxDeliveryAttempts = 10;
options.AutoDeclareDeadLetterExchange = true;
// Connection retry (initial attempts, then optionally indefinite)
options.InitialRetryAttempts = 5;
options.InitialRetryDelay = TimeSpan.FromSeconds(1);
options.MaxRetryDelay = TimeSpan.FromSeconds(120);
options.BackoffMultiplier = 2.0;
options.RetryIndefinitely = true;
});
// Add health checks (registers the Whizbang RabbitMQ health check)
builder.Services.AddRabbitMQHealthChecks();
Aspire Integration¶
Aspire Integration
// In AppHost project
var rabbitmq = builder.AddRabbitMQ("rabbitmq")
.WithManagementPlugin();
var api = builder.AddProject<Projects.MyApp_API>("api")
.WithReference(rabbitmq);
Aspire Integration (2)
// In API project - Aspire injects the connection string into configuration
builder.Services.AddRabbitMQTransport(
builder.Configuration.GetConnectionString("rabbitmq")!);
Azure Service Bus Configuration¶
Basic Setup¶
Basic Setup (2)
builder.Services.AddAzureServiceBusTransport(
builder.Configuration.GetConnectionString("servicebus")!);
Advanced Configuration¶
Advanced Configuration (2)
builder.Services.AddAzureServiceBusTransport(
builder.Configuration.GetConnectionString("servicebus")!,
options => {
// Infrastructure auto-provisioning (topics + subscriptions)
options.AutoProvisionInfrastructure = true;
options.DefaultSubscriptionName = "order-service";
// Consumer concurrency
options.MaxConcurrentCalls = 16;
options.PrefetchCount = 50;
// Session handling (per-stream ordered processing)
options.EnableSessions = true;
options.MaxConcurrentSessions = 200;
options.SessionIdleTimeout = TimeSpan.FromSeconds(1);
// Delivery / locks
options.MaxDeliveryAttempts = 10;
options.MaxAutoLockRenewalDuration = TimeSpan.FromMinutes(5);
// Connection retry
options.InitialRetryAttempts = 5;
options.InitialRetryDelay = TimeSpan.FromSeconds(1);
options.RetryIndefinitely = true;
});
// Add health checks (registers the Whizbang Service Bus health check)
builder.Services.AddAzureServiceBusHealthChecks();
Managed Identity Authentication¶
AddAzureServiceBusTransport takes a connection string, but it reuses any ServiceBusClient you have already registered. To authenticate with a managed identity, register the client yourself first:
Managed Identity Authentication
// Pre-register a ServiceBusClient using DefaultAzureCredential;
// the transport registration detects and reuses it.
builder.Services.AddSingleton(new ServiceBusClient(
"myapp.servicebus.windows.net",
new DefaultAzureCredential()));
builder.Services.AddAzureServiceBusTransport(
builder.Configuration.GetConnectionString("servicebus")!);
Migrating from Wolverine Transports¶
Wolverine RabbitMQ¶
Wolverine RabbitMQ
// Wolverine
builder.Host.UseWolverine(opts => {
opts.UseRabbitMq("amqp://guest:guest@localhost:5672")
.UseConventionalRouting()
.UseDurableOutbox();
});
Wolverine RabbitMQ (2)
// Whizbang
// - Outbox is built-in to Whizbang Core (no UseDurableOutbox equivalent needed)
// - Exchanges are auto-provisioned from routing configuration
builder.Services.AddRabbitMQTransport("amqp://guest:guest@localhost:5672");
Wolverine Azure Service Bus¶
Wolverine Azure Service Bus
// Wolverine
builder.Host.UseWolverine(opts => {
opts.UseAzureServiceBus(connectionString)
.UseTopicsAndSubscriptions()
.UseDurableOutbox();
});
Wolverine Azure Service Bus (2)
// Whizbang
// Topics and subscriptions are auto-provisioned from routing configuration
builder.Services.AddAzureServiceBusTransport(connectionString);
Message Routing¶
Routing is namespace-based: events publish to topics derived from their namespace, and commands route point-to-point through a shared inbox topic. Configure it with WithRouting on the builder chain:
Namespace-Based Routing
builder.Services
.AddWhizbang()
.WithRouting(routing => {
// Domains this service owns (its own command/event namespaces)
routing.OwnDomains("myapp.orders.commands", "myapp.orders.events");
// Or derive the namespace from a marker type
routing.OwnNamespaceOf<OrderCreated>();
// Event namespaces published by OTHER services that this service consumes
routing.SubscribeTo("myapp.payments.events");
routing.SubscribeToNamespaceOf<PaymentProcessed>();
// Commands arrive on a shared inbox topic (the default strategy)
routing.Inbox.UseSharedTopic("whizbang.inbox");
})
.AddTransportConsumer(); // Auto-generates transport subscriptions from routing config
Per-message-type topic overrides and SQL-style subscription filters are not part of the routing API — topics come from namespaces, and receive-side filtering happens automatically (messages with no local receptor or perspective are discarded at the receive boundary).
Testing Configuration¶
No Transport Needed for Local Tests¶
For unit and in-memory integration tests, skip transport registration entirely — local dispatch and the EF Core InMemory driver cover the full pipeline:
In-Memory Testing Without a Transport
public static class TestServices {
public static ServiceProvider Build() {
var services = new ServiceCollection();
// In-memory storage; no AddRabbitMQTransport/AddAzureServiceBusTransport call
services
.AddWhizbang()
.WithEFCore<TestDbContext>()
.WithDriver.InMemory;
return services.BuildServiceProvider();
}
}
Integration Test with TestContainers¶
Whizbang uses TUnit (not xUnit), so container lifecycle hooks use [Before(Test)]/[After(Test)] instead of IAsyncLifetime:
Integration Test with TestContainers
public class RabbitMqTransportTests {
private RabbitMqContainer _rabbitMq = null!;
[Before(Test)]
public async Task SetupAsync() {
_rabbitMq = new RabbitMqBuilder()
.WithImage("rabbitmq:3-management")
.Build();
await _rabbitMq.StartAsync();
}
[After(Test)]
public async Task TeardownAsync() {
await _rabbitMq.DisposeAsync();
}
private ServiceProvider BuildServices() {
var services = new ServiceCollection();
services.AddRabbitMQTransport(_rabbitMq.GetConnectionString());
return services.BuildServiceProvider();
}
}
Migration Checklist¶
- [ ] Add
SoftwareExtravaganza.Whizbang.Transports.RabbitMQand/orSoftwareExtravaganza.Whizbang.Transports.AzureServiceBuspackages - [ ] Configure environment-based transport switching
- [ ] Set up
appsettings.Development.jsonfor RabbitMQ - [ ] Set up
appsettings.Production.jsonfor Azure Service Bus - [ ] Add
AddRabbitMQHealthChecks()/AddAzureServiceBusHealthChecks() - [ ] Update Aspire integration (if using)
- [ ] Configure namespace-based routing with
WithRouting(...)andAddTransportConsumer() - [ ] Update in-memory integration tests to use
.WithDriver.InMemory(no transport registration)
Previous: Event Store Migration | Next: Outbox Migration