Upgrading

Version 8.3.2

MassTransit v8.3.2 drops the net6.0 target framework and adds the net9.0 target framework.

The RabbitMQ client v7 is now used, which no longer has support for batch publishing. This was purely a facade in the original client, and was therefore not implemented in the new client. You can learn more about the changes in the RabbitMQ Release.

Overall benchmarks of the new client with MassTransit yielded a 20-25% increase in message throughput.

Version 8.3

With MassTransit 8.3 there are a few core underlying changes that may require attention when upgrading from an earlier version.

Job Consumers

Support for recurring and scheduled job consumers is now available. To support these new features, the job saga state machines have new properties that must be persisted. The entity framework maps have been updated, to be sure to update any migrations in your applications and plan for the migrations to be run when or before the new versions are deployed.

The Job Consumer documentation has also been updated to include all new and existing features.

The NotifyCompleted, NotifyFaulted, NotifyCanceled, and NotifyStarted methods have been removed from the JobContext<T> interface. These were never meant to be called by a job consumer. The underlying job service components report the job state, there is no need for consumers to do it.

Job consumer behavior determines the resulting state:

  • If the Run method completes without throwing an exception, the job is Completed.
  • If the Run method throws an exception, the job is Faulted.
  • If the Run method throws an OperationCanceledException where the token is context.CancellationToken, the job is Canceled.

When checking for cancellation, or calling methods that can be canceled, the context.CancellationToken should be used. If checking for cancellation explicitly, using context.CancellationToken.IsCancellationRequested, the exception should be throwing using context.CancellationToken.ThrowIfCancellationRequested().

Transactional Outbox

The transactional outbox entity OutboxMessage now has foreign key relationships to InboxState and OutboxState. To meet this requirement, all three entity types must always be configured/included (previously you could leave one or the other off and the outbox would still function). The migrations should also be updated to include the new constraints.

Version 8.1

MassTransit version 8.1 is focused on improving cross-component integration between various components like the (mediator <-> bus, bus1 <-> bus2, etc). In previous versions of MassTransit, the ConsumeContext was used to send messages. This approach worked well for a long time, but as more components like the Mediator, MultiBus, and Riders became available, issues arose with resolving the correct ConsumeContext.

To address this issue, MassTransit v8.1 introduces a new capability to keep track of the owning component of the ConsumeContext. When the ConsumeContext is owned by another component, the library only copies necessary data such as headers, payloads, and source address. This change opens up the possibility of consuming message by the Mediator and sending it directly to the bus by resolving IPublishEndpoint or ISendEndpointProvider.

As this is a minor release, we have made every effort to ensure minimal impact on existing customer integrations. However, to use this capability, small changes are required. Previously, IServiceProvider was used as a parameter to most configuration methods, with this change IRegistrationContext should be used instead.

Sagas

In MassTransit v8.1, the registration of ISagaRepository<TSaga> in the container has been updated. Previously, this interface was responsible for both retrieving and querying sagas from the repository. With this release, we have decided to separate these responsibilities, resulting in the registering of two additional interfaces in container:

  • ILoadSagaRepository<TSaga> - should be used to load sagas by id.
  • IQuerySagaRepository<TSaga> - should be used to query saga ids by expression.

Both of these interfaces are registered in the container as singletons.

The registration ISagaRepository<TSaga> will be removed from the container, so it is recommended to start using these new interfaces instead.

Job Service State Machines

In Mass Transit v8.1 the job type saga now includes a name property. This may require a database migration depending on the persistence provider chosen.

Version 8

MassTransit v8 is the first major release since the availability of .NET 6. MassTransit v8 works a significant portion of the underlying components into a more manageable solution structure. Focused on the developer experience, while maintaining compatibility with previous versions, this release brings together the entire MassTransit stack.

Automatonymous, Green Pipes, and NewId have been completely integrated into a single MassTransit solution. This means that every aspect of MassTransit is now within a single namespace, which makes it easy to find the right interface, extension, and whatever else is needed. A lot of common questions result in a missing using statement, and now that should no longer be the case. The entire developer surface area, for the most part, exists within the MassTransit namespace.

When upgrading from previous versions of MassTransit, there are a few initial steps to get up and running. While this list doesn't cover everything, these are the main items experienced so far when upgrading from a previous version.

  • Remove any references to packages that were not updated with v8. This includes:
    • GreenPipes
    • NewId (still available separately, do not use in a project referencing MassTransit)
    • Automatonymous
    • Automatonymous.Visualizer -> MassTransit.StateMachineVisualizer
    • MassTransit.AspNetCore
    • MassTransit.Extensions.DependencyInjection
    • Any of the third-party container assemblies.
  • Remove any using statements that for namespaces that no longer exist

Some configuration interfaces have been removed/changed names:

OriginalNew
IServiceCollectionBusConfiguratorIBusRegistrationConfigurator
IServiceCollectionRiderConfiguratorIRiderRegistrationConfigurator
IServiceCollectionMediatorConfiguratorIMediatorRegistrationConfigurator

Serialization

The default JSON serializer is now System.Text.Json. Refer to Microsoft's Migration Guide if you encounter any serialization issues after upgrading.

To continue using Newtonsoft for serialization, add the MassTransit.Newtonsoft package and specify one of the configuration methods when configuring the bus:

  • UseNewtonsoftJsonSerializer
  • UseNewtonsoftRawJsonSerializer
  • UseXmlSerializer
  • UseBsonSerializer

AddMassTransitHostedService (deprecated)

Previous versions of MassTransit required the use of the MassTransit.AspNetCore package to support registration of MassTransit's hosted service. This package is no longer required, and MassTransit will automatically add an IHostedService for MassTransit.

The host can be configured using IOptions configuration support, such as shown below:

services.Configure<MassTransitHostOptions>(options =>
{
    options.WaitUntilStarted = true;
    options.StartTimeout = TimeSpan.FromSeconds(30);
    options.StopTimeout = TimeSpan.FromMinutes(1);
});

The .NET Generic Host has its own internal timers for shutdown, etc. that may also need to be adjusted.

In addition to the hosted service, .NET health checks are added as well, and may be included on health check endpoints.

Observers

Observers registered in the container will be connected to the bus automatically, including:

Observer TypeRegistration
IBusObserverAddBusObserver<T>
IReceiveObserverAddReceiveObserver<T>
IConsumeObserverAddConsumeObserver<T>
IReceiveEndpointObserverAddReceiveEndpointObserver<T>

State Machine Changes

The state machine interfaces, BehaviorContext<T> and BehaviorContext<T, TData> are now derived from SagaConsumeContext<T> and SagaConsumeContext<T, TMessage>. This significantly improves the usability of MassTransit features in state machine. No more calling GetPayload<ConsumeContext> or other methods to get access to the ConsumeContext! Seriously, this is awesome.

As part of this change, the .Data and .Instance properties of BehaviorContext are superfluous, and have subsequently been marked as obsolete. They still work, and return .Message or .Saga respectively, but eventually the might be removed (not in the near future though).

The previous Automatonymous.Activity<T> and Automatonymous.Activity<T, TData> interfaces have been renamed, and are now IStateMachineActivity<TSaga> and IStateMachineActivity<TSaga, TMessage> (both are now in the top-level MassTransit namespace).

Specifying headers when using the .Init<T>() message initializer with SendAsync, PublishAsync, and other related methods now works as expected!

The saga state machine test harness type IStateMachineSagaTestHarness<TInstance, TStateMachine> has been replaced with the properly named type ISagaStateMachineTestHarness<TStateMachine, TInstance>, which also has consistent generic argument ordering.

A new .Retry() activity has been added, allowing individual activities within a state machine to be retried. This retry is performed inline, with the same saga instance, and uses the same retry policies as message-based retry.

Nullable Types

MassTransit.Abstractions has enabled nullable type information, so it may signal to the compiler that a null can be returned when appropriate for certain methods.

Unit Testing

A new version of the test harness is now available, specifically designed for use with containers. The basics are the same, only the configuration has changed. An example test, shown below, using the in-memory transport by default. Consumer, saga, and activity test harnesses are added automatically and can be retrieved from the harness.

[Test]
public async Task The_consumer_should_respond_to_the_request()
{
    await using var provider = new ServiceCollection()
        .AddMassTransitTestHarness(x =>
        {
            x.AddConsumer<SubmitOrderConsumer>();
        })
        .BuildServiceProvider(true);

    var harness = provider.GetTestHarness();

    await harness.Start();

    var client = harness.GetRequestClient<SubmitOrder>();

    await client.GetResponse<OrderSubmitted>(new
    {
        OrderId = InVar.Id,
        OrderNumber = "123"
    });

    Assert.IsTrue(await harness.Sent.Any<OrderSubmitted>());

    Assert.IsTrue(await harness.Consumed.Any<SubmitOrder>());

    var consumerHarness = harness.GetConsumerHarness<SubmitOrderConsumer>();

    Assert.That(await consumerHarness.Consumed.Any<SubmitOrder>());
}
When the provider (which is an IServiceProvider) is disposed, it will dispose of the test harness, which will stop the bus.

Additionally, the test harness can now be used with any transport. For example, to use RabbitMQ:

[Test]
public async Task Should_use_broker()
{
    await using var provider = new ServiceCollection()
        .AddMassTransitTestHarness(x =>
        {
            x.AddConsumer<SubmitOrderConsumer>();

            x.UsingRabbitMq((context, cfg) =>
            {
                cfg.Host("some-broker-address", h =>
                {
                    h.Username("joe");
                    h.Password("cool");
                });

                cfg.ConfigureEndpoints(context);
            });
        })
        .BuildServiceProvider(true);
}

Third-Party Container Support

MassTransit is now using Microsoft.Extensions.DependencyInjection.Abstractions as an integral configuration component. This means that all configuration (such as AddMassTransit, AddMediator) is built against IServiceCollection. Support for other containers is provided using each specific container's extensions to work with IServiceCollection and IServiceProvider.

For example, using Autofac, the configuration might look something like what is shown below.

var collection = new ServiceCollection();

collection.AddMassTransit(x =>
{
    x.AddConsumer<SubmitOrderConsumer>();

    x.UsingRabbitMq((context, cfg) => 
    {
        cfg.ConfigureEndpoints(context);
    });
});
var factory = new AutofacServiceProviderFactory();
var container = factory.CreateBuilder(collection);

return factory.CreateServiceProvider(container);

MassTransit would then be able to use IServiceProvider with Autofac to create scopes, resolve dependencies, etc.

Transport Changes

  • RabbitMQ batch publishing is now disabled by default. If you are seeing a degradation in publishing performance after upgrading, you may benefit from enabling batch publish to increase throughput. Scenarios where batching can improve throughput include high-latency broker connectivity and HA/Lazy queues.

Version 7

As with previous major versions, MassTransit V7 includes a number of new features, but has also deprecated or changed from previous configuration syntax. For the most part, consumers, sagas, etc. should work exactly as they did with previous releases. However, some of the configuration aspects may have been updated to be more consistent.

.NET Standard 2.0

MassTransit is now built for .NET Standard 2.0 only. The packages should be compatible with .NET Standard 2.0 (or later), as well as .NET Framework 4.6.1 (or later). Specific .NET Framework packages are no longer built or packaged.

Riders

Riders are an entirely new feature which adds Kafka and Azure Event Hub support to MassTransit. A huge thanks to Denys Kozhevnikov GitHub @noonamer for his amazing effort!

Configuration

Configuring MassTransit using a container has been streamlined. Refer to the configuration section for details. A brief summary:

  • The .Host methods are now void. The IHost interfaces are no longer accessible (or needed).
  • AddBus has been superseded by UsingRabbitMq (and other transport-specific extension methods)

Sagas

The SagaRepository standardization is now completed, and all other repositories have been removed (InMemorySagaRepository is still there though).

Send topology can be configured for a message to use a CorrelationId, allowing saga state machine to automatically configure events that correlate on a property that isn't implemented by CorrelatedBy<Guid>.

Message Scheduler

A number of new container configuration options for configuring and registering the message scheduler have been added.

Turnout

Turnout, which has been poorly supported since the beginning, has been rewritten from the ground up. Consumers can now use the IJobConsumer<T> interface to support long-running jobs managed by MassTransit. They are supported using Conductor, and a series of state machines to track job execution, retry, and concurrency. Check out the job consumers section for details.

Mediator

  • Container configuration has changed, and now uses the AddMediator method (instead of AddMassTransit).
  • Publish no longer throws if there are no consumers. To throw when publishing and no consumers are registered, set the Mandatory flag on the PublishContext.
  • Consumers can now be connected/detached after the mediator has been created.

Testing

  • Test harnesses now use an inactivity timer to complete sooner once the bus stops processing messages.
  • Message lists, such as Consumed, Received, Sent, and Published, now have async Any methods

Transactions

The transaction outbox has been renamed to TransactionalBus, to avoid confusion. See the transactions section for details.

Changed, Deprecated

The following packages have been deprecated and replaced with a new package:

The following packages have been deprecated and are no longer supported:

Version 6

Automatonymous

In previous version, using Automatonymous required an additional package, MassTransit.Automatonymous. The contents of that package are now included in the MassTransit assembly/package, which now depends on Automatonymous. This was done to reduce the number of extra packages required for container support (along with state machine registration), as well as improve the saga repository persistence assemblies.

When upgrading to v6, any references to the old MassTransit.Automatonymous package should be removed.

If you are using a container with MassTransit, and were using one of the old container packages for Automatonymous, those package references should also be removed. With version 6, only the single container integration package is required (such as MassTransit.Autofac or MassTransit.Extensions.DependencyInjection).

The following packages are available for the supported containers:

  • MassTransit.Autofac
  • MassTransit.Extensions.DependencyInjection
  • MassTransit.SimpleInjector
  • MassTransit.StructureMap
  • MassTransit.Windsor

Saga Repository Update (v6.1+)

The saga repositories have been completely refactored, to eliminate duplicate logic and increase consistency across the various storage engines. All repositories also now support the container registration extensions, which provides a consistent syntax for registering and configuring saga repositories for use with dependency injection containers. When using the .AddMassTransit() container registration method, a repository can now be registered with the saga. For details, see the updated documentation.

Azure Service Bus

The previous (now legacy) MassTransit.AzureServiceBus package, which was only maintained to continue support for .NET 4.5.2, has been deprecated. Going forward, the MassTransit.Azure.ServiceBus.Core package should be used. The package supports both .NET 4.6.1 and .NET Standard 2.0. With the new package, the .NET Messaging protocol is no longer supported. The new package includes both AMQP and WebSocket support. Certain corporate firewall configurations that previously used .NET Messaging instead of AMQP may need to specify the web socket protocol to connect to Azure Service Bus.

Logging

The previous log abstraction used by MassTransit has been replaced with Microsoft.Extensions.Logging.Abstractions.

The previous log integration packages for Log4Net, NLog, and Serilog have been deprecated. An ILoggerFactory instance can be configured for MassTransit by calling:

LogContext.ConfigureCurrentLogContext(loggerFactory);

This should be done prior to configuring the bus.

If you are using the new .AddMassTransit() configuration, combined with .AddBus(), then ILoggerFactory is automatically configured for you. In this case, the statement above is not required.

DiagnosticSource

As of version 6, MassTransit now uses DiagnosticSource for tracking messaging operations, such as Send, Receive, Publish, Consume, etc. An Activity is created for each operation, and context-relevant tags and baggage are added.

MassTransit follows the guidance from Microsoft. To connect listeners, look at the section that explains how to connect.

Receive Endpoint Configuration

When MassTransit underwent a major overhaul, and multiple host support was added, that seemed like a great idea. A single bus talking to more than one broker, doing messaging. Reality &emdot; nobody used it. It added a lot of complexity, that wasn't used.

With version 6, a single bus has a single host. That's it. Simple. And with this change, it is no longer necessary to specify the host when configuring a receive endpoint. Yes, the old methods are there, and a pseudo-host is returned from the .Host() call which can still be passed, but it is ignored. All the transport-specific configuration methods are still there, without the host parameter.

So, enjoy the simplicity. Under the covers some other things were also made simple &emdot; but I doubt you'll notice.

Courier

To be consistent with the rest of MassTransit, many of the interfaces in Courier has been renamed. For example, ExecuteActivity<TArguments> is now IExecuteActivity<TArguments>. The previous interfaces are still supported, but have been marked obsolete.