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
TIP
Lamar is more "forgiving" than the built in ServiceProvider
. If after converting to Wolverine 3.0, you receive messages from ServiceProvider
about not being able to resolve this, that, or the other, just go back to Lamar with the steps in this guide.
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:
services.AddMarten(opts =>
{
opts.Connection(Servers.PostgresConnectionString);
opts.DisableNpgsqlLogging = true;
})
.IntegrateWithWolverine(w =>
{
w.MessageStorageSchemaName = "public";
w.TransportSchemaName = "public";
})
.ApplyAllDatabaseChangesOnStartup();
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.
WARNING
rabbitmq-dotnet-client
v7 is currently marked as an RC release and suitable for testing in pre-production environments. Wolverine.RabbitMq
will continue to be marked as an RC release until rabbitmq-dotnet v7 is fully stable.
If you use another RabbitMQ wrapper in your application, hold off on upgrading until it also supports v7.
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 fromFanOut
. 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.
- The previous
BindExchange()
syntax was renamed toDeclareExchange()
to better reflect Rabbit MQ operations
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:
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();
Also for Wolverine.Http users, the [Document]
attribute behavior in the Marten integration is now "required by default."
Azure Service Bus
The Azure Service Bus will now "sanitize" any queue/subscription names to be all lower case. This may impact your usage of conventional routing. Please report any problems with this to GitHub.
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:
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));
}
}