This is part of the NServiceBus Upgrade Guide from Version 8 to 9, which also includes the following individual upgrade guides for specific components:
Transports
- AmazonSQS Transport Upgrade Version 6 to 7
- Azure Service Bus Transport Upgrade Version 3 to 4
- SQL Server Transport Upgrade Version 7 to 8
Hosting
Other
Removed support for .NET Framework
NServiceBus 9 no longer supports any version of the .NET Framework. Instead, it targets .NET 8 only (read more about the supported frameworks and platforms). Any component in NServiceBus 8 that is .NET Framework only (for example, the MSMQ transport) will not have a version that is compatible with NServiceBus 9. NServiceBus 8 will continue to be supported for use on the .NET Framework.
Serializer choice is now mandatory
The XML serializer is no longer the default serializer, so a serializer must always be configured.
SendOptions immediate dispatch changes
The method used to determine if immediate dispatch has been requested for a message has been renamed.
9.x NServiceBus
class MyBehavior : Behavior<IOutgoingContext>
{
public override Task Invoke(IOutgoingContext context, Func<Task> next)
{
var sendOptions = context.Extensions.Get<SendOptions>();
if (sendOptions.IsImmediateDispatchSet())
{
// do something
}
return next();
}
}
8.x NServiceBus
class MyBehavior : Behavior<IOutgoingContext>
{
public override Task Invoke(IOutgoingContext context, Func<Task> next)
{
var sendOptions = context.Extensions.Get<SendOptions>();
if (sendOptions.RequiredImmediateDispatch())
{
// do something
}
return next();
}
}
IManageUnitsOfWork has been removed
The IManageUnitsOfWork
API has been removed. Instead, a pipeline behavior should be used to implement custom units of work.
9.x NServiceBus
class MyUnitOfWork : Behavior<IIncomingPhysicalMessageContext>
{
public override async Task Invoke(IIncomingPhysicalMessageContext context, Func<Task> next)
{
// start the custom unit of work
try
{
await next();
}
catch (Exception ex)
{
// handle exception
}
// end the custom unit of work
}
}
8.x NServiceBus
class MyUnitOfWork : IManageUnitsOfWork
{
public Task Begin()
{
// start the custom unit of work
return Task.CompletedTask;
}
public Task End(Exception ex = null)
{
// end the custom unit of work
if (ex != null)
{
// handle exception
}
return Task.CompletedTask;
}
}
API to override machine name has changed
The RuntimeEnvironment.
API Override the machine name has been removed. The replacement API is the HostInfoSettings.
method.
9.x NServiceBus
var endpointConfiguration = new EndpointConfiguration("MyEndpoint");
endpointConfiguration.UniquelyIdentifyRunningInstance()
.UsingHostName("MyMachineName");
8.x NServiceBus
RuntimeEnvironment.MachineNameAction = () => "MyMachineName";
API to set additional audit metadata has changed
The API to set additional audit metadata has been changed.
9.x NServiceBus
public class MyAuditDataBehavior : Behavior<IAuditContext>
{
public override Task Invoke(IAuditContext context, Func<Task> next)
{
context.AuditMetadata["myKey"] = "MyValue";
return next();
}
}
8.x NServiceBus
public class MyAuditDataBehavior : Behavior<IAuditContext>
{
public override Task Invoke(IAuditContext context, Func<Task> next)
{
context.AddAuditData("myKey","MyValue");
return next();
}
}
Dependency registration access in features renamed
The property used to access container registrations in features has been renamed from Container
to Services
.
9.x NServiceBus
protected override void Setup(FeatureConfigurationContext context)
{
context.Services.AddSingleton<MySingleton>();
}
8.x NServiceBus
protected override void Setup(FeatureConfigurationContext context)
{
context.Container.AddSingleton<MySingleton>();
}
Service collection extensions for backward compatibility removed
Service collection extensions to ease the transition to Microsoft DI abstractions have been removed. It is now required to use the registration APIs added in NServiceBus 8.
9.x NServiceBus
// 1
services.Add(new ServiceDescriptor(typeof(MyDependency), typeof(MyDependency), ServiceLifetime.Singleton));
// 2
services.AddSingleton<MyDependency>();
// 3
services.AddScoped<MyDependency>();
// 4
services.AddTransient<MyDependency>();
// 5
services.AddSingleton(new MyDependency());
// 6
if (services.AsEnumerable().Any(serviceDescriptor => serviceDescriptor.ServiceType == typeof(MyDependency)))
{
// do something
}
8.x NServiceBus
// 1
services.ConfigureComponent(typeof(MyDependency), DependencyLifecycle.SingleInstance);
// 2
services.ConfigureComponent<MyDependency>(DependencyLifecycle.SingleInstance);
// 3
services.ConfigureComponent<MyDependency>(DependencyLifecycle.InstancePerUnitOfWork);
// 4
services.ConfigureComponent<MyDependency>(DependencyLifecycle.InstancePerCall);
// 5
services.RegisterSingleton(new MyDependency());
// 6
if(services.HasComponent<MyDependency>())
{
// do something
}
Endpoint addresses
In NServiceBus version 8 and earlier, the local transport-specific queue addresses are accessible via the settings.
and settings.
settings extension methods. These extension methods have been replaced with a variety of new APIs, depending on the scenario of where the addresses are needed.
Accessing logical addresses in features
Since endpoint addresses are translated to transport-specific ones later during endpoint startup, addresses are defined using a transport-agnostic QueueAddress
type. The addresses can be accessed via the FeatureConfigurationContext
:
9.x NServiceBus
class MyFeature : Feature
{
protected override void Setup(FeatureConfigurationContext context)
{
QueueAddress local = context.LocalQueueAddress();
QueueAddress instance = context.InstanceSpecificQueueAddress();
}
}
8.x NServiceBus
class MyFeature : Feature
{
protected override void Setup(FeatureConfigurationContext context)
{
string local = context.Settings.LocalAddress();
string instance = context.Settings.InstanceSpecificQueue();
}
}
Accessing the endpoint's receive addresses
Inject the ReceiveAddresses
type to access the endpoint's receive addresses.
class StartupTask(ReceiveAddresses receiveAddresses) : FeatureStartupTask
{
protected override Task OnStart(IMessageSession session, CancellationToken cancellationToken = default)
{
// equivalent to settings.LocalAddress()
Console.WriteLine($"Starting endpoint, listening on {receiveAddresses.MainReceiveAddress}");
if (receiveAddresses.InstanceReceiveAddress != null)
{
// equivalent to settings.InstanceSpecificQueue())
Console.WriteLine($"Starting endpoint, listening on {receiveAddresses.InstanceReceiveAddress}");
}
return Task.CompletedTask;
}
protected override Task OnStop(IMessageSession session, CancellationToken cancellationToken = default) => Task.CompletedTask;
}
Dynamic address translation
Instead of using settings.
, inject the ITransportAddressResolver
type to translate a QueueAddress
to a transport-specific address at runtime.
9.x NServiceBus
public class MyHandler(ITransportAddressResolver addressResolver) : IHandleMessages<MyMessage>
{
public Task Handle(MyMessage message, IMessageHandlerContext context)
{
var destination = addressResolver.ToTransportAddress(new QueueAddress("Sales"));
var sendOptions = new SendOptions();
sendOptions.SetDestination(destination);
return context.Send(new SomeMessage(), sendOptions);
}
}
8.x NServiceBus
public class MyHandler : IHandleMessages<MyMessage>
{
readonly IReadOnlySettings settings;
public MyHandler(IReadOnlySettings settings)
{
this.settings = settings;
}
public Task Handle(MyMessage message, IMessageHandlerContext context)
{
var destination = settings.Get<TransportDefinition>().ToTransportAddress(new QueueAddress("Sales"));
var sendOptions = new SendOptions();
sendOptions.SetDestination(destination);
return context.Send(new SomeMessage(), sendOptions);
}
}
Extensibility
This section describes changes to advanced extensibility APIs.
Making features depend on message driven subscriptions
The API to make features depend on message-driven subscriptions when implementing custom persisters has changed:
9.x NServiceBus
class MyFeature : Feature
{
public MyFeature() => DependsOn("NServiceBus.Features.MessageDrivenSubscriptions");
protected override void Setup(FeatureConfigurationContext context)
{
// setup my feature
}
}
8.x NServiceBus
class MyFeature : Feature
{
public MyFeature() => DependsOn<MessageDrivenSubscriptions>();
protected override void Setup(FeatureConfigurationContext context)
{
// setup my feature
}
}
The extension point for event-based notifications has been removed
NServiceBus 8 already replaced the event-based error notifications with task-based callbacks. The extension point for custom event-based notifications has been removed in NServiceBus 9. Any custom notifications should be converted. See error notification events for more details.