Getting Started
Architecture
NServiceBus
Transports
Persistence
Hosting
ServiceInsight
ServicePulse
ServiceControl
Monitoring
Modernization
Samples

Upgrade Version 10 to 11

Component: NServiceBus

Upgrading a major dependency like NServiceBus requires careful planning, see the general recommendations article to learn more about the optimal upgrade process.

Self-hosted endpoints

With the ubiquity of the .NET Generic Host as the entry point for an application's hosting, dependency injection, and logging needs, it no longer makes sense to self-host NServiceBus endpoints using Endpoint.Create() or Endpoint.Start(). Instead, NServiceBus endpoints can be added to the IServiceCollection which will cause them to start along with the host's lifecycle.

Instead of:

var endpointInstance = await Endpoint.Start(endpointConfiguration);

// or

var startableEndpoint = await Endpoint.Create(endpointConfiguration);
var endpointInstance = await startableEndpoint.Start();

…the endpoint can be started through the .NET Generic Host:

var builder = Host.CreateApplicationBuilder();

builder.Services.AddNServiceBusEndpoint(endpointConfiguration);

var host = builder.Build();

await host.RunAsync();

In addition, the following APIs related to creating and starting endpoints with the self-hosting API are deprecated and no longer necessary when using the .NET Generic Host:

  • NServiceBus.Endpoint
  • NServiceBus.Installer
  • NServiceBus.IEndpointInstance
  • NServiceBus.IStartableEndpoint
  • NServiceBus.IStartableEndpointWithExternallyManagedContainer

Endpoint-specific dependency injection

The RegisterCompoments(Action<IServiceCollection> registration) method on EndpointConfiguration is obsolete and must be replaced. Originally this method was meant to allow dependency injection registrations when self-hosting, but is no longer necessary without self-hosted endpoints. It is better practice to manage dependency injection registrations using standard .NET idioms through the Generic Host.

Instead of:

var endpointConfiguration = new EndpointConfiguration("EndpointName");
endpointConfiguration.RegisterComponents(registrations =>
{
    registrations.AddSingleton<EndpointSpecificService>();
});

…the service can be added to the global IServiceCollection when only one NServiceBus endpoint is defined, and the endpoint will resolve the dependency from the global collection:

var endpointConfiguration = new EndpointConfiguration("EndpointName");

builder.Services.AddSingleton<EndpointSpecificService>();
builder.Services.AddNServiceBusEndpoint(endpointConfiguration);

When multiple endpoints are hosted in the same process, each endpoint can receive its own configured dependency using keyed services, where the key matches the endpoint name by default:

var salesConfig = new EndpointConfiguration("Sales");
var billingConfig = new EndpointConfiguration("Billing");

var salesDb = new DatabaseService("sales-db");
var billingDb = new DatabaseService("billing-db");

builder.Services.AddKeyedSingleton<DatabaseService>(salesConfig.EndpointName, salesDb);
builder.Services.AddKeyedSingleton<DatabaseService>(billingConfig.EndpointName, billingDb);

builder.Services.AddNServiceBusEndpoint(salesConfig, "Sales");
builder.Services.AddNServiceBusEndpoint(billingConfig, "Billing");

Logging

NServiceBus now uses Microsoft.Extensions.Logging as its built-in logging infrastructure. When no other logging providers are configured, NServiceBus provides opinionated defaults: a rolling file logger and a colored console logger, just like the previous DefaultFactory did, but now powered by the Microsoft.Extensions.Logging pipeline. As soon as other logging providers are registered on the host, these built-in providers automatically disable themselves so that the externally configured providers take over without any manual opt-out.

The legacy NServiceBus logging configuration APIs have been deprecated and will produce compiler warnings. These APIs will cause compile errors in NServiceBus version 11 and will be removed in NServiceBus version 12.

Migrating from LogManager.GetLogger to ILogger

LogManager.GetLogger is not deprecated yet, but it will be deprecated in a future version. It should be replaced with Microsoft.Extensions.Logging, which offers two approaches depending on the application structure. For more details, see Logging in .NET.

Dependency injection (recommended)

In applications that use dependency injection or a host, ILogger<T> should be obtained through constructor injection. This is the primary and recommended pattern in Microsoft.Extensions.Logging.

Instead of:

public class MyHandler : IHandleMessages<MyMessage>
{
    static ILog log = LogManager.GetLogger(typeof(MyHandler));

    public Task Handle(MyMessage message, IMessageHandlerContext context)
    {
        log.Info("Handling message");
        return Task.CompletedTask;
    }
}

use constructor-injected ILogger<T>:

public class MyHandler(ILogger<MyHandler> logger) : IHandleMessages<MyMessage>
{
    public Task Handle(MyMessage message, IMessageHandlerContext context)
    {
        logger.LogInformation("Handling message");
        return Task.CompletedTask;
    }
}
Static logging (non-hosted contexts)

In scenarios where dependency injection is not available, such as before the host is built or in static contexts, an ILoggerFactory can be created directly following the guidance in Get started with .NET logging:

using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger("MyCategory");
logger.LogInformation("Message logged before the host is built.");

This approach is suitable only for trivial scenarios. In non-trivial applications, ILoggerFactory and ILogger should be obtained from the DI container rather than created directly, as described in Integration with hosts and dependency injection.

High-performance logging

For high-performance scenarios, the LoggerMessage source generator creates strongly-typed logging methods that avoid unnecessary allocations and string formatting overhead.

Using custom logging providers

Instead of LogManager.Use<T>() or LogManager.UseFactory(…), register custom logging providers directly with the Microsoft.Extensions.Logging infrastructure on the host:

var builder = Host.CreateApplicationBuilder();

builder.Logging.AddSerilog();

Configuring the rolling file logger

The RollingLoggerProviderOptions section is only relevant when relying on NServiceBus default logging. If the host already has an external logging provider configured, the built-in rolling file and console log providers are automatically disabled and these options have no effect.

Instead of using DefaultFactory, configure the rolling file logger through the options pattern:

var builder = Host.CreateApplicationBuilder();

builder.Services.Configure<RollingLoggerProviderOptions>(options =>
{
    options.Directory = "C:/logs";
    options.LogLevel = LogLevel.Debug;
    options.NumberOfArchiveFilesToKeep = 10;
    options.MaxFileSizeInBytes = 10L * 1024 * 1024;
});

Direct logger retrieval from DefaultFactory

Calling DefaultFactory.GetLoggingFactory().GetLogger(...) is no longer supported and will throw an exception at runtime. Use ILogger<T> via dependency injection instead.

Deprecated APIs

The following APIs are deprecated:

Deprecated APIReplacement
LogManager.Use<T>()Register an ILoggerProvider via IServiceCollection
LogManager.UseFactory(ILoggerFactory)Configure Microsoft.Extensions.Logging directly on the host
DefaultFactoryservices.Configure<RollingLoggerProviderOptions>()
DefaultFactory.Directory(string)RollingLoggerProviderOptions.Directory
DefaultFactory.Level(LogLevel)RollingLoggerProviderOptions.LogLevel
LoggingFactoryDefinitionImplement ILoggerProvider and register via services.AddSingleton<ILoggerProvider, YourProvider>()

Host identifier algorithm change

In version 11, the default algorithm for generating deterministic host identifiers changes from MD5 to XxHash128 (RFC 9562 version 8 GUIDs). This produces different host identifiers, which affects how endpoints are identified in ServicePulse and ServiceControl. Changing the algorithm will cause existing known endpoints to appear inactive in the ServicePulse heartbeats and monitoring views, while new instances (with the changed host identifiers) appear in their place.

Rationale

This change avoids using MD5 for default host identifier generation, which prevents FIPS policy enforcement from blocking endpoint startup for this code path (see FIPS compliance). The legacy MD5-based algorithm is not appropriate for this non-cryptographic use case.

To ensure a predictable transition, this is designed as a multi-phase migration:

NServiceBus VersionsHashes AvailableDefault HashApp Switch
<= 10.2MD5 OnlyMD5-
>= 10.2 && < 11.0MD5 + XxHash128MD5Can opt in
>= 11.0 && < 12.0MD5 + XxHash128XxHash128Can opt out
>= 12.0XxHash128 OnlyXxHash128-

In version 11, XxHash128 becomes the default. The opt-out switch is intended as a temporary migration aid when operational dashboards, log queries, audit processing, or monitoring processes need more time to move from the legacy generated host identifiers to the new identifiers.

This approach allows the framework to move away from MD5-based host identifier generation while providing flexibility to manage existing integrations before the legacy algorithm is removed in version 12.

Impact

After upgrading, endpoints that rely on the default generated host identifier will receive new host identifiers. This causes endpoints to appear as new entries in ServicePulse, while the previous instances become stale and must be removed from the monitoring view.

The changed host identifier also affects any custom logging, audit processing, dashboards, or queries that use generated host identifier headers, such as $.diagnostics.hostid or $.diagnostics.originating.hostid. Endpoint names, queues, message processing, and explicitly configured host identifiers are not affected.

ServiceControl stores and displays the resulting host identifier value. It does not know whether two different host identifiers were generated from the same endpoint path and machine name by different algorithms.

Temporarily preserving the legacy generated host identifier

To temporarily preserve the existing MD5-based generated host identifier after upgrading to version 11, set the following AppContext switch before endpoint startup:

AppContext.SetSwitch("NServiceBus.Core.Hosting.UseV2DeterministicGuid", false);

Or via environment variable:

DOTNET_NServiceBus_Core_Hosting_UseV2DeterministicGuid=false

Or via MSBuild in the project file:

<ItemGroup>
  <RuntimeHostConfigurationOption Include="NServiceBus.Core.Hosting.UseV2DeterministicGuid" Value="false" />
</ItemGroup>

If an endpoint must keep a specific host identifier beyond version 11, configure the host identifier explicitly instead of relying on the legacy algorithm switch. For example, an endpoint can be configured with its existing host identifier to keep the value stable after the legacy algorithm is removed. See Overriding the host identifier.