Skip to content

Migration Guide

Key Changes in 3.0

The 3.0 release did not have any breaking changes to the public API, but does come with some significant internal changes.

Lamar Removal

The biggest change is that Wolverine is no longer directly coupled to the Lamar IoC library and Wolverine will no longer automatically replace the built in ServiceProvider with Lamar. At this point it is theoretically possible to use Wolverine with any IoC library that fully supports the ASP.Net Core DI conformance behavior, but Wolverine has only been tested against the default ServiceProvider and Lamar IoC containers.

Do be aware if moving to Wolverine 3.0 that Lamar is more forgiving than ServiceProvider, so there might be some hiccups if you choose to forgo Lamar. See the Configuration Guide for more information. Lamar does still have a little more robust support for the code generation abilities in Wolverine (Wolverine uses the IoC configuration to generate code to inline dependency creation in a way that's more efficient than an IoC tool at runtime -- when it can).

TIP

If you have any issues with Wolverine's code generation about your message handlers or HTTP endpoints after upgrading to Wolverine 3.0, please open a GitHub issue with Wolverine, but just know that you can probably fall back to using Lamar as the IoC tool to "fix" those issues with code generation planning.

Wolverine 3.0 can now be bootstrapped with the HostApplicationBuilder or any standard .NET bootstrapping mechanism through IServiceCollection.AddWolverine(). The limitation of having to use IHostBuilder is gone.

Marten Integration

The Marten/Wolverine IntegrateWithWolverine() integration syntax changed from a lot of optional arguments to a single call with a nested lambda registration like this:

cs
services.AddMarten(opts =>
    {
        opts.Connection(Servers.PostgresConnectionString);
        opts.DisableNpgsqlLogging = true;
    })
    .IntegrateWithWolverine(w =>
    {
        w.MessageStorageSchemaName = "public";
        w.TransportSchemaName = "public";
    })
    .ApplyAllDatabaseChangesOnStartup();

snippet source | anchor

All Marten/Wolverine integration options are available by this one syntax call now, with the exception of event subscriptions.

Wolverine.RabbitMq

The RabbitMq transport recieved a significant overhaul for 3.0.

RabbitMq Client v7

The RabbitMq .NET client has been updated to v7, bringing with it an internal rewrite to support async I/O and vastly improved memory usage & throughput. This version also supports OTEL out of the box.

Conventional Routing Improvements

  • Queue bindings can now be manually overridden on a per-message basis via BindToExchange, this is useful for scenarios where you wish to use conventional naming between different applications but need other exchange types apart from FanOut. This should make conventional routing the default usage in the majority of situations. See Conventional Routing for more information.
  • Conventional routing entity creation has been split between the sender and receive side. Previously the sender would generate all exchange and queue bindings, but now if the sender has no handlers for a specific message, the queues will not be created.

General RabbitMQ Improvements

  • Added support for Headers exchange
  • Queues now apply bindings instead of exchanges. This is an internal change and shouldn't result in any obvious differences for users.
  • The configuration model has expanded flexibility with Queues now bindable to Exchanges, alongside the existing model of Exchanges binding to Queues.

Sagas

Wolverine 3.0 added optimistic concurrency support to the stateful Saga support. This will potentially cause database migrations for any Marten-backed Saga types as it will now require the numeric version storage.

Leader Election

The leader election functionality in Wolverine has been largely rewritten and should eliminate the issues with poor behavior in clusters or local debugging time usage where nodes do not gracefully shut down. Internal testing has shown a significant improvement in Wolverine's ability to detect node changes and rollover the leadership election.

Wolverine.PostgresSql

The PostgreSQL transport option requires you to explicitly set the transportSchema, or Wolverine will fall through to using wolverine_queues as the schema for the database backed queues. Wolverine will no longer use the envelope storage schema for the queues.

Wolverine.Http

For Wolverine.Http usage, the Wolverine 3.0 usage of the less capable ServiceProvider instead of the previously mandated Lamar library necessitates the usage of this API to register necessary services for Wolverine.HTTP in addition to adding the Wolverine endpoints:

cs
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

// Necessary services for Wolverine HTTP
// And don't worry, if you forget this, Wolverine
// will assert this is missing on startup:(
builder.Services.AddWolverineHttp();

snippet source | anchor

Also for Wolverine.Http users, the [Document] attribute behavior in the Marten integration is now "required by default."

Messaging

The behavior of IMessageBus.InvokeAsync<T>(message) changed in 3.0 such that the T response is not also published as a message at the same time when the initial message is sent with request/response semantics. Wolverine has gone back and forth in this behavior in its life, but at this point, the Wolverine thinks that this is the least confusing behavioral rule.

You can selectively override this behavior and tell Wolverine to publish the response as a message no matter what by using the new 3.0 [AlwaysPublishResponse] attribute like this:

cs
public class CreateItemCommandHandler
{
    // Using this attribute will force Wolverine to also publish the ItemCreated event even if
    // this is called by IMessageBus.InvokeAsync<ItemCreated>()
    [AlwaysPublishResponse]
    public async Task<(ItemCreated, SecondItemCreated)> Handle(CreateItemCommand command, IDocumentSession session)
    {
        var item = new Item
        {
            Id = Guid.NewGuid(),
            Name = command.Name
        };

        session.Store(item);

        return (new ItemCreated(item.Id, item.Name), new SecondItemCreated(item.Id, item.Name));
    }
}

snippet source | anchor

Released under the MIT License.