Upgrade Version 5 to 6

Component: NServiceBus

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

Move to .NET 4.5.2

The minimum .NET version for NServiceBus Version 6 is .NET 4.5.2.

All projects (that reference NServiceBus) must be updated to .NET 4.5.2 before updating to NServiceBus Version 6.

It is recommended to update to .NET 4.5.2 and perform a full migration to production before updating to NServiceBus Version 6. This will help isolate any issues that may occur.

For solutions with many projects the Visual Studio extension Target Framework Migrator can reduce the manual effort required in performing an upgrade.

See also:

While a minimum of .NET 4.5.2 is required, it is recommended to update to at least .NET 4.6.1 since this gives access to Task.CompletedTask.

Update NServiceBus dependencies

All NServiceBus dependencies for an endpoint project are managed via NuGet. Open the Manage NuGet Packages window for the endpoint project, switch to the Updates tab and look for packages that start with NServiceBus. Update each one to the latest Version 6 package.

Once packages have been updated the project will contain quite a few errors. This is expected as a lot of things have changed.

See also:

Update Endpoint configuration

In previous versions of NServiceBus, to connect a process to the transport, an instance of IBus was needed. In Version 6 and above, this concept has been deprecated and now an instance of IEndpointInstance is required. The code required to create and configure an IEndpointInstance is very similar to the code found in Version 5 endpoints for creating and configuring IBus instances.

This section describes updating a self-hosted endpoint. For endpoints that rely on the NServiceBus Host, see: NServiceBus Host Upgrade Version 6 to 7.

First, change all mentions of BusConfiguration to EndpointConfiguration. Note that EndpointConfiguration has a required constructor parameter to set the endpoint name. In Version 5, the name of the endpoint was provided via the .EndpointName(name) method on the BusConfiguration class. This call is no longer required in Version 6 and the method has been deprecated.

Most of the other method calls on EndpointConfiguration should continue to work the same way as they did on BusConfiguration. The methods that have changed between versions will each have deprecation messages that describe how to achieve the same effect in Version 6.

Once the instance of EndpointConfiguration has been created, it can be used to create an IEndpointInstance. In Version 5 and below, this step is accomplished using the Bus static class. In Version 6, this has been replaced with an Endpoint static class that works in a similar manner.

In Version 6 and above, any operation that interacts with the transport is asynchronous and returns a Task. This includes the Start method on the static Endpoint class and the Stop method on IEndpointInstance. Ideally these methods are called from within an async method and the results can simply be awaited (with ConfigureAwait(false) applied to them).

6.x NServiceBus
Edit
async Task Run(EndpointConfiguration endpointConfiguration)
{
    // pre startup
    var endpointInstance = await Endpoint.Start(endpointConfiguration)
        .ConfigureAwait(false);
    // post startup

    // block process

    // pre shutdown
    await endpointInstance.Stop()
        .ConfigureAwait(false);
    // post shutdown
}

If this is not the case then these calls must be converted back into synchronous ones using GetAwaiter().GetResult(). It is recommended that this conversion occurs early in the application lifecycle.

6.x NServiceBus
Edit
void Run(EndpointConfiguration endpointConfiguration)
{
    RunAsync(endpointConfiguration).GetAwaiter().GetResult();
}

async Task RunAsync(EndpointConfiguration endpointConfiguration)
{
    // pre startup
    var endpointInstance = await Endpoint.Start(endpointConfiguration)
        .ConfigureAwait(false);
    // post startup

    // block process

    // pre shutdown
    await endpointInstance.Stop()
        .ConfigureAwait(false);
    // post shutdown
}

Note that in Version 5 and below, IBus implements IDisposable and stops communicating with the transport when Dispose is called. It has been common to call Bus.Create from within a using block in console applications. In Version 6 as above, stopping an instance of an endpoint is asynchronous and needs to return a Task which is not possible with the signature of IDisposable. IEndpointInstance does not implement IDisposable and explicitly calling Stop and awaiting the returned Task is the only way to shut down the endpoint.

See also:

Update Handlers

In Version 6 the signature of IHandleMessages<T> has been changed to support asynchronous processing. In Version 5 and below, each message was handled by a dedicated thread. This meant that NServiceBus was able to take advantage of thread static state to keep track of the message being handled as well as the current transaction. In Version 6, each message is handled by a task which may run on several threads before processing is completed. Rather than rely on thread context and state, each handler is explicitly passed in a context object with all of the information it needs to execute.

This context includes methods to send and publish new messages. As NServiceBus can no longer rely on thread static state to access information about the message and transaction being handled, it is not possible to rely on an injected copy of IBus to perform these operations. It is necessary to always send and publish new messages using the context object rather than an injected instance of the endpoint.

As each handler is running in the context of a transport receive operation (an I/O context) and is likely to contain other asynchronous operations (like sends and publishes), all handlers must return a Task.

To update a handler to Version 6 follow this process:

  1. As the signature of IHandleMessages<T> has changed, Visual Studio will complain that the handler is not implementing it. To correctly implement the handler interface, change the return type of the Handle method from void to async Task. Next add a second parameter to the Handle method IMessageHandlerContext context.
  2. If the handler has an instance of IBus injected into it, it needs to be removed. Prior to removing it, rename it to context as all operations that previously relied on IBus will now go through the passed in instance of IMessageHandlerContext.
  3. Finally, the methods on IMessageHandlerContext all return tasks. It is important to await each of these tasks and to add .ConfigureAwait(false) on to each one.

See also:

Update Sagas

Updating a saga is very similar to updating a handler with just a few extra steps.

The Saga<T> base class has been moved from to the NServiceBus namespace. Remove all using statements that refer to NServiceBus.Saga.

Update all of the IHandleMessages<T> implementations using the process outlined in the previous section. Note that IAmStartedByMessages<T> implementations can be updated in the same manner.

Check the implementation of ConfigureHowToFindSaga(). NServiceBus will be able to automatically correlate any message that is a reply to a message originally sent by this saga. For any other message type, including messages that can start the saga, an explicit mapping is required.

Remove the [Unique] attribute from the saga data class. NServiceBus will now automatically make correlated saga properties unique.

Note that calls to RequestTimeout() now require an instance IMessageHandlerContext to be passed in. Pass in the context parameter that was passed in to the Handle() method. Additionally, this method returns a Task which should have ConfigureAwait(false) applied and then wait for the response with await.

See also:

Sending and Publishing outside of a handler

Once all of the handlers and sagas in an endpoint have been updated to Version 6 there may still be places in the code that send and publish messages using an instance of IBus that need to be updated.

All message handlers and sagas that are included in the endpoint should be updated before taking this step. It is important that the techniques presented here are not used from inside message handlers or transactional consistency with the transport is not guaranteed.

The endpoint instance returned from Endpoint.Create() or Endpoint.Start() implements IMessageSession which contains Send() and Publish() methods that can be used outside of a message handler or saga. If the endpoint sends messages in the same part of the code that creates/starts the endpoint then call these methods on the returned endpoint instance directly.

As the Send and Publish methods on the endpoint instance should not be used from within a handler or saga, there is no implementation of the interface injected into the configured IoC container. For recommendations on how to get access to IMessageSession in other locations, see Dependency Injection.

See also:

Final steps

This covers the basic steps required to update an endpoint to Version 6. Each of the other NServiceBus dependencies may also require additional steps. See the dependency specific upgrade guides for more information.

Hosting

Transports

Persistence

Others

Related Articles


Last modified