# Upgrade Version 8 to 9 Upgrading a major dependency like NServiceBus requires careful planning, see the [general recommendations](/nservicebus/upgrades/index.md) article to learn more about the optimal upgrade process. ## 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](/nservicebus/upgrades/supported-platforms.md)). 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](/nservicebus/serialization/index.md#configuring-a-serializer). ## SendOptions immediate dispatch changes The method used to determine if [immediate dispatch](/nservicebus/messaging/send-a-message.md#dispatching-a-message-immediately) has been requested for a message has been renamed. ```cs class MyBehavior : Behavior { public override Task Invoke(IOutgoingContext context, Func next) { var sendOptions = context.Extensions.Get(); if (sendOptions.IsImmediateDispatchSet()) { // do something } return next(); } } ``` ```cs class MyBehavior : Behavior { public override Task Invoke(IOutgoingContext context, Func next) { var sendOptions = context.Extensions.Get(); 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](/nservicebus/pipeline/unit-of-work.md#implementing-custom-unit-of-work). ```cs class MyUnitOfWork : Behavior { public override async Task Invoke(IIncomingPhysicalMessageContext context, Func next) { // start the custom unit of work try { await next(); } catch (Exception ex) { // handle exception } // end the custom unit of work } } ``` ```cs 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.MachineNameAction` API [Override the machine name](/nservicebus/hosting/override-machine-name.md) has been removed. The replacement API is the `HostInfoSettings.UsingHostName` method. ```cs var endpointConfiguration = new EndpointConfiguration("MyEndpoint"); endpointConfiguration.UniquelyIdentifyRunningInstance() .UsingHostName("MyMachineName"); ``` ```cs RuntimeEnvironment.MachineNameAction = () => "MyMachineName"; ``` ## API to set additional audit metadata has changed The API to [add additional audit metadata](/nservicebus/operations/auditing.md#adding-additional-audit-information) has been changed. ```cs public class MyAuditDataBehavior : Behavior { public override Task Invoke(IAuditContext context, Func next) { context.AuditMetadata["myKey"] = "MyValue"; return next(); } } ``` ```cs public class MyAuditDataBehavior : Behavior { public override Task Invoke(IAuditContext context, Func 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`. ```cs protected override void Setup(FeatureConfigurationContext context) { context.Services.AddSingleton(); } ``` ```cs protected override void Setup(FeatureConfigurationContext context) { context.Container.AddSingleton(); } ``` ## Service collection extensions for backward compatibility removed Service collection extensions to ease [the transition to Microsoft DI abstractions](/nservicebus/upgrades/7to8/dependency-injection.md#registercomponents-changes) have been removed. It is now required to use the registration APIs added in NServiceBus 8. ```cs // 1 services.Add(new ServiceDescriptor(typeof(MyDependency), typeof(MyDependency), ServiceLifetime.Singleton)); // 2 services.AddSingleton(); // 3 services.AddScoped(); // 4 services.AddTransient(); // 5 services.AddSingleton(new MyDependency()); // 6 if (services.AsEnumerable().Any(serviceDescriptor => serviceDescriptor.ServiceType == typeof(MyDependency))) { // do something } ``` ```cs // 1 services.ConfigureComponent(typeof(MyDependency), DependencyLifecycle.SingleInstance); // 2 services.ConfigureComponent(DependencyLifecycle.SingleInstance); // 3 services.ConfigureComponent(DependencyLifecycle.InstancePerUnitOfWork); // 4 services.ConfigureComponent(DependencyLifecycle.InstancePerCall); // 5 services.RegisterSingleton(new MyDependency()); // 6 if(services.HasComponent()) { // do something } ``` ## Endpoint addresses In NServiceBus version 8 and earlier, the local transport-specific queue addresses are accessible via the `settings.LocalAddress()` and `settings.InstanceSpecificQueue()` 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`: ```cs class MyFeature : Feature { protected override void Setup(FeatureConfigurationContext context) { QueueAddress local = context.LocalQueueAddress(); QueueAddress instance = context.InstanceSpecificQueueAddress(); } } ``` ```cs 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. ```cs 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.Get().ToTransportAddress(myAddress)`, inject the `ITransportAddressResolver` type to translate a `QueueAddress` to a transport-specific address at runtime. ```cs public class MyHandler(ITransportAddressResolver addressResolver) : IHandleMessages { 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); } } ``` ```cs public class MyHandler : IHandleMessages { readonly IReadOnlySettings settings; public MyHandler(IReadOnlySettings settings) { this.settings = settings; } public Task Handle(MyMessage message, IMessageHandlerContext context) { var destination = settings.Get().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](/nservicebus/messaging/publish-subscribe/index.md#mechanics-message-driven-persistence-based) when implementing custom [persisters](/persistence/index.md) has changed: ```cs class MyFeature : Feature { public MyFeature() => DependsOn("NServiceBus.Features.MessageDrivenSubscriptions"); protected override void Setup(FeatureConfigurationContext context) { // setup my feature } } ``` ```cs class MyFeature : Feature { public MyFeature() => DependsOn(); 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](/nservicebus/upgrades/7to8/index.md#error-notification-events) for more details.