# Migrate handlers and sagas to Version 6 The handler method on `IHandleMessages` now returns a Task. To leverage async code, add the `async` keyword to the handler method and use `await` for async methods. To convert the synchronous code add `return Task.FromResult(0);` or `return Task.CompletedTask` (.NET 4.6 and higher) to the handler methods. > [!WARNING] > Do not `return null` from the message handlers. Returning `null` will result in an Exception. ```csharp // For NServiceBus version 6.x public class UpgradeMyAsynchronousHandler : IHandleMessages { public Task Handle(MyMessage message, IMessageHandlerContext context) { return SomeLibrary.SomeAsyncMethod(message); } } public class UpgradeMySynchronousHandler : IHandleMessages { public Task Handle(MyMessage message, IMessageHandlerContext context) { // when no asynchronous code is executed in a handler // Task.CompletedTask can be returned SomeLibrary.SomeMethod(message.Data); return Task.CompletedTask; } } // For NServiceBus version 5.x public class UpgradeMessageHandler : IHandleMessages { public void Handle(MyMessage message) { SomeLibrary.SomeMethod(message.Data); } } ``` ## API Changes ### Bus Send and Receive There is a change in the parameters, giving access to the `IMessageHandlerContext`, which provides the methods that used to be called from `IBus`. Use the `IMessageHandlerContext` to send and publish messages. ```csharp public class SendAndPublishHandler : IHandleMessages { public async Task Handle(MyMessage message, IMessageHandlerContext context) { await context.Send(new MyOtherMessage()); await context.Publish(new MyEvent()); } } ``` ### Message handler ordering In Version 6 the message handler ordering APIs are simplified. The full API can be seen in [Handler ordering](/nservicebus/handlers/handler-ordering.md). #### Specifying a Handler to run first ```csharp // For NServiceBus version 6.x endpointConfiguration.ExecuteTheseHandlersFirst(typeof(HandlerB)); // For NServiceBus version 5.x public class MySpecifyingFirst : ISpecifyMessageHandlerOrdering { public void SpecifyOrder(Order order) { order.SpecifyFirst(); } } ``` #### Specifying Handler order ```csharp // For NServiceBus version 6.x endpointConfiguration.ExecuteTheseHandlersFirst( typeof(HandlerB), typeof(HandlerA), typeof(HandlerC)); // For NServiceBus version 5.x busConfiguration.LoadMessageHandlers(First.Then().AndThen()); ``` ### New context arguments The signature for the mutators now passes context arguments that give access to relevant information on the message and also on the mutation of the message. This context gives access to the same functionality as previous versions so update the code accordingly. See [header manipulation](/nservicebus/messaging/header-manipulation.md) for one example on how this might look. ### HandleCurrentMessageLater and the Outbox The `HandleCurrentMessageLater` method can no longer be used in conjunction with the [Outbox](/nservicebus/outbox/index.md). When this scenario is detected an exception with the following message is thrown: > HandleCurrentMessageLater cannot be used in conjunction with the Outbox. Use the recoverability mechanisms or delayed delivery instead. Use the [recoverability](/nservicebus/recoverability/index.md) or [delayed delivery](/nservicebus/messaging/delayed-delivery.md) APIs instead when using the Outbox. ## Migration In previous versions of NServiceBus, a typical handler class looks like the below: ```csharp public class MigrationBeginning : IHandleMessagesFromPreviousVersions { public IBus Bus { get; set; } public void Handle(MyMessage message) { Bus.Send(new MyOtherMessage()); Bus.Publish(new MyEvent()); } } ``` Implement the new `IHandleMessages` interface. Remove any code that is generated by the IDE or additional tools like Resharper and mark the new `Handle` method as `async` by adding the `async` keyword. ```csharp public class MigrationStep1 : IHandleMessages { public IBus Bus { get; set; } public void Handle(MyMessage message) { Bus.Send(new MyOtherMessage()); Bus.Publish(new MyEvent()); } public async Task Handle(MyMessage message, IMessageHandlerContext context) { } } ``` Rename the bus property or the bus constructor parameter to `context`. Although the [IBus interface has been deprecated](moving-away-from-ibus.md) and can be safely removed, for this step renaming the property instead of deleting it comes in useful when refactoring existing code. It's much better to rename all the existing references from `bus` property to `context`. ```csharp public class MigrationStep2 : IHandleMessages { public IBus context { get; set; } public void Handle(MyMessage message) { context.Send(new MyOtherMessage()); context.Publish(new MyEvent()); } public async Task Handle(MyMessage message, IMessageHandlerContext context) { } } ``` Inline or "cut-and-paste" the old `Handle` method code into the new asynchronous `Handle` method. When the code is compiled a compiler warning [`CS4014`](https://msdn.microsoft.com/en-us/library/hh873131.aspx) will be shown indicating that there are asynchronous methods in the handler which are not awaited. ```csharp public class MigrationStep3 : IHandleMessages { public async Task Handle(MyMessage message, IMessageHandlerContext context) { // CS4014: Consider applying the 'await' operator to the result of the call. context.Send(new MyOtherMessage()); context.Publish(new MyEvent()); } } ``` Fix the compiler warnings by introducing the `await` statement to each asynchronous method call. > [!NOTE] > Visual Studio 2015 and higher has the capability to automatically fix those warnings with the [`Ctrl+.`](https://msdn.microsoft.com/en-us/library/dn872466.aspx) (depending on the keybindings) shortcut. It is even possible to fix it in the whole solution if desired. ```csharp public class MigrationStep4 : IHandleMessages { public async Task Handle(MyMessage message, IMessageHandlerContext context) { await context.Send(new MyOtherMessage()); await context.Publish(new MyEvent()); } } ``` After these steps start moving other code in the handler towards async if the code supports it when it is desired to fully leverage async/await. For example with Entity Framework instead of calling `SaveChanges` call `SaveChangesAsync` on the database context. For information about how to migrate handlers with dependencies that access the `IBus` interface, refer to [IBus interface has been deprecated](moving-away-from-ibus.md) guidance. ## Saga API Changes ### Remove NServiceBus.Saga namespace The `NServiceBus.Saga` namespace has been removed to stop it clashing with the `NServiceBus.Saga.Saga` class. For all commonly used APIs (e.g., the `Saga` class and `IContainSagaData` interface) they have been moved into the `NServiceBus` namespace. Other more advanced APIs (e.g., the `IFinder` and `IHandleSagaNotFound` interfaces) have been moved into the `NServiceBus.Sagas` namespace. In most cases `using NServiceBus.Saga` can be replaced with `using NServiceBus`. ### Unique attribute no longer needed NServiceBus automatically makes the correlated saga property unique without the need for an explicit `[Unique]` attribute to be used. This attribute can be safely removed from saga data types. ### ConfigureHowToFindSaga All messages that start the saga (`IAmStartedByMessages`) need to be mapped to the saga data using either a mapping in `ConfigureHowToFindSaga` method, or a custom [saga finder](/nservicebus/sagas/saga-finding.md); otherwise, an exception is thrown on endpoint startup. Other messages that are handled by the saga (`IHandleMessages`) also require mappings, unless they are reply messages resulting from a message sent out of the saga, in which case they contain the SagaId in a message header. Messages that cannot be mapped by a SagaId message header, by a property mapping in `ConfigureHowToFindSaga`, or via a custom saga finder will throw a runtime exception. In the below example, the `OrderSaga` is started by the `StartOrder` message. The `OrderSaga` also handles the `CompleteOrder` message. ```csharp public class OrderSaga : Saga, IAmStartedByMessages, IHandleMessages ``` In Version 6, the `StartOrder` message needs to be specified in the `ConfigureHowToFindSaga` method. ```csharp protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper) { mapper.ConfigureMapping(message => message.OrderId) .ToSaga(sagaData => sagaData.OrderId); mapper.ConfigureMapping(message => message.OrderId) .ToSaga(sagaData => sagaData.OrderId); } ``` ### Correlating properties Version 6 automatically correlates incoming message properties to its saga data counterparts. Any saga data correlation in the message handler code can be safely removed. Correlated properties (for existing saga instances) do not change once set. > [!NOTE] > Correlated properties must have a non [default](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/default-values-table) value, i.e., not null and not empty, assigned when persisted. If not the following exception is thrown: > > ``` > The correlated property 'MyPropery' on Saga 'MySaga' does not have a value. > A correlated property must have a non-default (i.e., non-null and non-empty) value assigned when a new saga instance is created. > ``` > > Use a [custom finder](/nservicebus/sagas/saga-finding.md) for the received message to override this validation. ```csharp // For NServiceBus version 6.x public async Task Handle(StartOrder message, IMessageHandlerContext context) { // The processing logic for the StartOrder message } // For NServiceBus version 5.x public void Handle(StartOrder message) { Data.OrderId = message.OrderId; // The processing logic for the StartOrder message } ``` Versions 6 and above only support correlating messages to a single saga property. Correlating on more than one property is still supported by creating a custom [saga finder](/nservicebus/sagas/saga-finding.md). If sagas with multiple correlations mappings to different properties are detected the following exception is thrown: ``` Sagas can only have mappings that correlate on a single saga property. Use custom finders to correlate *message types* to Saga *saga type* ``` ### Saga persisters & finders Saga persisters (`ISagaPersister`) and finders (`IFindSagas`) introduce a new parameter `SagaPersistenceOptions`. This parameter gives access to the saga metadata and pipeline context. The options enable persisters and finders to manipulate everything that exists in the context during message pipeline execution. For more information see [Sagas](/nservicebus/sagas/index.md) and [Complex saga finding logic](/nservicebus/sagas/saga-finding.md). ### MarkAsComplete no longer virtual The `Saga` base class method `MarkAsComplete` is no longer virtual. ### RequestTimeout requires IMessageHandlerContext `RequestTimeout` requires a `IMessageHandlerContext` as additional parameter. Pass the context argument received in the handle method to `RequestTimeout`. ### ReplyToOriginator requires IMessageHandlerContext `ReplyToOriginator` requires a `IMessageHandlerContext` as additional parameter. Pass the context argument received in the handle method to `RequestTimeout`.