Azure Service Bus¶
The most fully-featured KnightBus transport: queues, topics with subscriptions, native dead-lettering, and deferred messages that can be cancelled.
dotnet add package KnightBus.Azure.ServiceBus
dotnet add package KnightBus.Azure.ServiceBus.Messages
Registration¶
services
.UseServiceBus(config =>
{
config.FullyQualifiedNamespace = "yournamespace.servicebus.windows.net";
config.Credential = new ManagedIdentityCredential();
})
.RegisterProcessors()
.UseTransport<ServiceBusTransport>();
Leave ConnectionString unset when using a credential. Supplying neither a connection string nor
a namespace-plus-credential throws at startup.
Messages¶
| Interface | Kind |
|---|---|
IServiceBusCommand |
Command — one queue, one consumer |
IServiceBusEvent |
Event — one topic, many subscriptions |
public class OrderPlaced : IServiceBusEvent
{
public string OrderId { get; set; }
}
public class OrderPlacedMapping : IMessageMapping<OrderPlaced>
{
public string QueueName => "order-placed";
}
Queues, topics and subscriptions are created automatically the first time they are needed.
Client¶
using var scope = host.Services.CreateScope();
var bus = scope.ServiceProvider.GetRequiredService<IServiceBus>();
| Method | Notes |
|---|---|
SendAsync<T>(T message) |
Send one command. |
SendAsync<T>(IEnumerable<T> messages) |
Batched send. |
PublishEventAsync<T>(T message) |
Publish one event. |
PublishEventsAsync<T>(IEnumerable<T> messages) |
Batched publish. |
ScheduleAsync<T>(T message, TimeSpan span) |
Deferred send. Returns a sequence number. |
ScheduleAsync<T>(IEnumerable<T> messages, TimeSpan span) |
Batched deferred send. Returns one sequence number per message, in input order. |
CancelScheduledAsync<T>(long sequenceNumber) |
Cancel a deferred message. |
It is PublishEventAsync, not PublishAsync
Service Bus is the one transport whose publish methods are named after events. Every method takes
an optional CancellationToken.
Deferred messages¶
The delay is relative, and the returned sequence number is the only handle for cancelling:
var sequenceNumber = await bus.ScheduleAsync(
new SendReminder { UserId = userId },
TimeSpan.FromHours(2)
);
await bus.CancelScheduledAsync<SendReminder>(sequenceNumber);
Batched scheduling is atomic — either all messages are scheduled or none are. Scheduling is available for commands only; there is no deferred publish for events.
Entity creation options¶
To control how KnightBus creates the queue or topic, implement IServiceBusCreationOptions on the
message's mapping:
public class OrderPlacedMapping : IMessageMapping<OrderPlaced>, IServiceBusCreationOptions
{
public string QueueName => "order-placed";
public bool EnablePartitioning => true;
public bool SupportOrdering => false;
public bool EnableBatchedOperations => true;
}
| Option | Default | Effect |
|---|---|---|
EnablePartitioning |
false |
Creates the entity as partitioned, for higher throughput. |
SupportOrdering |
false |
Messages are forwarded to subscriptions in order. |
EnableBatchedOperations |
true |
Server-side batching. |
Two caveats:
- It is all or nothing. A mapping that implements the interface supplies all three values; there is no per-property merge with the defaults.
- It only applies at creation. Changing these values does not reconfigure an entity that already exists — Azure requires most of them to be set when the entity is created.
To change the defaults for every message instead, adjust DefaultCreationOptions on the
configuration:
services.UseServiceBus(config =>
{
config.ConnectionString = connectionString;
config.DefaultCreationOptions.EnablePartitioning = true;
});
The property itself is read-only on IServiceBusConfiguration, so mutate the existing options object
rather than assigning a new one. To assign one, build a concrete ServiceBusConfiguration and pass it
to the other overload:
services.UseServiceBus(new ServiceBusConfiguration
{
ConnectionString = connectionString,
DefaultCreationOptions = new ServiceBusCreationOptions { EnablePartitioning = true },
});
Processing settings¶
Settings map onto Service Bus' own processor options. Messages are received in peek-lock mode and
completed by KnightBus, and MessageLockTimeout becomes the maximum auto lock-renewal duration —
which means Service Bus renews the lock for you up to that limit. This is why
ExtendMessageLockDurationMiddleware is neither needed nor supported here.
DeadLetterDeliveryLimit must be lower than the queue's own MaxDeliveryCount, or Service Bus
dead-letters the message before KnightBus does and the
IProcessBeforeDeadLetter<T> hook never
runs.
Dead letters¶
Service Bus provides a dead letter sub-queue per queue and per subscription. Read and requeue them with the management API:
This is also the only transport supporting PeekScheduled (inspecting deferred messages before
delivery) and CancelScheduledMessage through the management API.
Attachments¶
Service Bus messages are size-limited, so large payloads belong in attachments. This package ships no attachment provider, which is not a gap — the provider is a host-wide choice, and either of the two that exist can back attachments here. Blob Storage is the usual pick:
services
.UseServiceBus(config => config.ConnectionString = connectionString)
.UseBlobStorage(storageConnectionString)
.UseBlobStorageAttachments()
.UseTransport<ServiceBusTransport>();
Note that only UseTransport<ServiceBusTransport>() appears — UseBlobStorage(...) supplies the
storage account for the attachments and does not start any Storage Queues listener. The same pattern
gives Service Bus messages sagas and
singleton processing, which likewise ship in other packages.
Serialization¶
Defaults to NewtonsoftSerializer. See serialization to change it.
Example¶
KnightBus.Samples.Azure.ServiceBus
demonstrates commands, events with two subscriptions, the dead letter hook, creation options,
management and OpenTelemetry. A separate producer example and an Aspire host that runs both against
the Service Bus emulator sit alongside it.