Configuration Extensions
::: warning As of Wolverine 3.0, it is no longer possible to alter service registrations through Wolverine extensions that are themselves registered in the IoC container at bootstrapping time. :::
Wolverine supports the concept of extensions for modularizing Wolverine configuration with implementations of the IWolverineExtension interface:
/// <summary>
/// Use to create loadable extensions to Wolverine applications
/// </summary>
public interface IWolverineExtension : IJasperFxExtension
{
/// <summary>
/// Make any alterations to the WolverineOptions for the application
/// </summary>
/// <param name="options"></param>
void Configure(WolverineOptions options);
}snippet source | anchor
Here’s a sample:
public class SampleExtension : IWolverineExtension
{
public void Configure(WolverineOptions options)
{
// Add service registrations
options.Services.AddTransient<IFoo, Foo>();
// Alter settings within the application
options
.UseNewtonsoftForSerialization(settings => settings.TypeNameHandling = TypeNameHandling.None);
}
}snippet source | anchor
Extensions can be applied programmatically against the WolverineOptions like this:
using var host = await Host.CreateDefaultBuilder()
.UseWolverine(opts =>
{
// Including a single extension
opts.Include<SampleExtension>();
// Or add a Wolverine extension that needs
// to use IoC services
opts.Services.AddWolverineExtension<ConfigurationUsingExtension>();
})
.ConfigureServices(services =>
{
// This is the same logical usage, just showing that it
// can be done directly against IServiceCollection
services.AddWolverineExtension<ConfigurationUsingExtension>();
})
.StartAsync();snippet source | anchor
Lastly, you can also add IWolverineExtension types to your IoC container registration that will be applied to WolverineOptions just
before bootstrapping Wolverine at runtime. This was originally added to allow for test automation scenarios where you might want
to override part of the Wolverine setup during tests. As an example, consider this common usage for disabling external transports
during testing:
// This is using Alba to bootstrap a Wolverine application
// for integration tests, but it's using WebApplicationFactory
// to do the actual bootstrapping
await using var host = await AlbaHost.For<Program>(x =>
{
// I'm overriding
x.ConfigureServices(services => services.DisableAllExternalWolverineTransports());
});snippet source | anchor
Behind the scenes, Wolverine has a small extension like this:
internal class DisableExternalTransports : IWolverineExtension
{
public void Configure(WolverineOptions options)
{
options.ExternalTransportsAreStubbed = true;
}
}snippet source | anchor
And that extension is just added to the application’s IoC container at test bootstrapping time like this:
public static IServiceCollection DisableAllExternalWolverineTransports(this IServiceCollection services)
{
services.AddSingleton<IWolverineExtension, DisableExternalTransports>();
return services;
}snippet source | anchor
In usage, the IWolverineExtension objects added to the IoC container are applied after the inner configuration
inside your application’s UseWolverine() set up.
As another example, IWolverineExtension objects added to the IoC container can also use services injected into the
extension object from the IoC container as shown in this example that uses the .NET IConfiguration service:
public class ConfigurationUsingExtension : IWolverineExtension
{
private readonly IConfiguration _configuration;
// Use constructor injection from your DI container at runtime
public ConfigurationUsingExtension(IConfiguration configuration)
{
_configuration = configuration;
}
public void Configure(WolverineOptions options)
{
// Configure the wolverine application using
// the information from IConfiguration
}
}snippet source | anchor
There’s also a small helper method to register Wolverine extensions like so:
Modifying Transport Configuration
If your Wolverine extension needs to apply some kind of extra configuration to the transport integration, most of the
transport packages support a WolverineOptions.ConfigureTransportName() extension method that will let you make
additive configuration changes to the transport integration for items like declaring extra queues, topics, exchanges, subscriptions or overriding
dead letter queue behavior. For example:
ConfigureRabbitMq()ConfigureKafka()ConfigureAzureServiceBus()ConfigureAmazonSqs()
Asynchronous Extensions
::: tip This was added to Wolverine 2.3, specifically for a user needing to use the Feature Flag library from Microsoft. :::
There is also any option for creating Wolverine extensions that need to use asynchronous methods to configure
the WolverineOptions using the IAsyncWolverineExtension library. A sample is shown below:
public class SampleAsyncExtension : IAsyncWolverineExtension
{
private readonly IFeatureManager _features;
public SampleAsyncExtension(IFeatureManager features)
{
_features = features;
}
public async ValueTask Configure(WolverineOptions options)
{
if (await _features.IsEnabledAsync("Module1"))
{
// Make any kind of Wolverine configuration
options
.PublishMessage<Module1Message>()
.ToLocalQueue("module1-high-priority")
.Sequential();
}
}
}snippet source | anchor
Which can be added to your application with this extension method on IServiceCollection:
using var host = await Host.CreateDefaultBuilder()
.UseWolverine(opts =>
{
opts.Services.AddFeatureManagement();
opts.Services.AddSingleton(featureManager);
// Adding the async extension to the underlying IoC container
opts.Services.AddAsyncWolverineExtension<SampleAsyncExtension>();
}).StartAsync();snippet source | anchor
Asynchronous Extensions and Wolverine.HTTP
Just a heads up, there’s a timing issue between the application of asynchronous Wolverine extensions
and the usage of the Wolverine.HTTP MapWolverineEndpoints() method. If you need the asynchronous
extensions to apply to the HTTP configuration, you need to help Wolverine out by explicitly calling
this method in your Program file after building the WebApplication, but before calling
MapWolverineEndpoints() like so:
var app = builder.Build();
// In order for async Wolverine extensions to apply to Wolverine.HTTP configuration,
// you will need to explicitly call this *before* MapWolverineEndpoints()
await app.Services.ApplyAsyncWolverineExtensions();snippet source | anchor
Wolverine Plugin Modules
::: tip Use this sparingly, but it might be advantageous for adding extra instrumentation or extra middleware :::
If you want to create a Wolverine extension assembly that automatically loads itself into an application just
by being referenced by the project, you can use a combination of IWolverineExtension and the [WolverineModule]
assembly attribute.
Assuming that you have an implementation of IWolverineExtension named Module1Extension, you can mark your module library
with this attribute to automatically add that extension to Wolverine:
[assembly: WolverineModule<Module1Extension>]snippet source | anchor
You can also use the non-generic [assembly: WolverineModule] form to mark an assembly purely as a handler
module — its handlers are discovered, but no IWolverineExtension is applied.
How discovery works (source generator, not runtime scanning)
::: tip
Module discovery moved to a compile-time source generator in Wolverine 6.0 (GH-2902). Earlier versions
probed the application’s bin directory at startup (AssemblyFinder), which was slower and not AOT/trim-friendly.
:::
The [WolverineModule] and [WolverineModule<T>] attributes both derive from JasperFx’s [JasperFxAssembly]
attribute. The JasperFx.SourceGenerator analyzer — which the WolverineFx package flows transitively to
every project that references it (directly or through any Wolverine extension/transport package) — sees these
assembly attributes at compile time and emits a small JasperFx.Generated.DiscoveredExtensions manifest into
the assembly listing the declared extension type(s). At startup Wolverine reads those generated manifests from the
loaded assemblies instead of scanning the filesystem and speculatively Assembly.Load-ing candidates.
A few consequences worth knowing:
- Only the type declared in
[WolverineModule<T>](or[WolverineModule(typeof(T))]) is auto-applied. OtherIWolverineExtensionimplementations sitting in the same assembly are not activated automatically — register those explicitly withopts.Include<T>()orservices.AddWolverineExtension<T>(). - The extension assembly must be compiled against WolverineFx 6.0+ so that the analyzer runs and emits its manifest. A module built against an older Wolverine will not be auto-discovered; reference it and call its configuration explicitly instead.
- Wolverine walks the application’s reference graph to make sure referenced module assemblies (for example
WolverineFx.RuntimeCompilation) are loaded before reading their manifests, so “reference the package and it just activates” still works. This deliberately does not glob the bin directory the way the old scan did.
Disabling Assembly Scanning
::: info
As of Wolverine 6.0 there is no runtime bin-directory assembly scan for extensions — discovery is driven by the
compile-time manifest described above. ExtensionDiscovery.ManualOnly remains the switch to turn automatic module
discovery off entirely.
:::
If you want a marginally faster start up, or you simply want full control over which extensions are applied, you can disable automatic extension discovery (the reference-graph walk and manifest read) with this syntax:
using var host = await Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder()
.UseWolverine(opts =>
{
opts.DisableConventionalDiscovery();
// With ExtensionDiscovery.ManualOnly, Wolverine does not auto-load the
// WolverineFx.RuntimeCompilation module, so a TypeLoadMode.Dynamic app must
// opt into runtime Roslyn compilation explicitly (or pre-generate with Static).
opts.UseRuntimeCompilation();
}, ExtensionDiscovery.ManualOnly)
.StartAsync();snippet source | anchor