Autofac Integration
Set up Nimbus with Autofac for dependency injection in .NET applications
Overview
Nimbus has first-class Autofac support via the Nimbus.Containers.Autofac package. It provides two key extension methods:
builder.RegisterNimbus(typeProvider)— registers Nimbus components (type provider, dependency resolver, all handler types) in the Autofac containerconfiguration.WithAutofacDefaults(componentContext)— connects the built container to the bus configuration
Installation
dotnet add package Nimbus.Containers.Autofac
Basic Setup
The typical setup registers everything in the Autofac container, then hands the container to the bus:
using Autofac;
using Nimbus;
using Nimbus.Configuration;
using Nimbus.Infrastructure;
using Nimbus.Transports.Redis;
// 1. Scan assemblies for message types and handlers
var typeProvider = new AssemblyScanningTypeProvider(
typeof(PlaceOrderCommand).Assembly,
typeof(PlaceOrderHandler).Assembly
);
// 2. Build the Autofac container
var builder = new ContainerBuilder();
// Register Nimbus — wires up type provider, DI resolver, and all handler types
builder.RegisterNimbus(typeProvider);
// Register your own services
builder.RegisterType<OrderRepository>()
.As<IOrderRepository>()
.InstancePerLifetimeScope();
builder.RegisterType<EmailService>()
.As<IEmailService>()
.InstancePerLifetimeScope();
var container = builder.Build();
// 3. Configure and build the bus
var bus = new BusBuilder()
.Configure()
.WithTransport(new RedisTransportConfiguration()
.WithConnectionString("localhost:6379"))
.WithNames("OrderService", Environment.MachineName)
.WithAutofacDefaults(container) // connects Autofac to the bus
.Build();
await bus.Start();
What RegisterNimbus Does
builder.RegisterNimbus(typeProvider) performs three registrations:
- Registers the
ITypeProvideras a singleton — Nimbus uses this to discover message types and handler types - Registers
AutofacDependencyResolverasIDependencyResolver— adapts Autofac to Nimbus’s DI abstraction - Registers all resolvable types from the type provider (your handler types) as
InstancePerLifetimeScope
This means you don’t need to register your handlers manually — RegisterNimbus discovers and registers them automatically from the assemblies you pass to AssemblyScanningTypeProvider.
What WithAutofacDefaults Does
configuration.WithAutofacDefaults(container) resolves ITypeProvider and IDependencyResolver from the container and passes them to the bus configuration. It’s equivalent to:
configuration
.WithTypesFrom(container.Resolve<ITypeProvider>())
.WithDependencyResolver(container.Resolve<IDependencyResolver>());
Handler Lifetime
Handlers registered by RegisterNimbus are InstancePerLifetimeScope. Nimbus creates a child lifetime scope for each message, so each message gets its own handler instance and a fresh set of scoped dependencies:
Container (root scope)
│
├── Message 1 arrives → child scope created
│ ├── PlaceOrderHandler (new instance)
│ ├── IOrderRepository (new instance, scoped)
│ └── Disposed after handler completes
│
└── Message 2 arrives → child scope created
├── PlaceOrderHandler (new instance)
├── IOrderRepository (new instance, scoped)
└── Disposed after handler completes
Registering Interceptors
Register interceptors in the container before building it. Nimbus resolves them per-message from the child scope:
builder.RegisterType<LoggingInterceptor>()
.AsSelf()
.InstancePerLifetimeScope();
builder.RegisterType<MetricsInterceptor>()
.AsSelf()
.InstancePerLifetimeScope();
var container = builder.Build();
var bus = new BusBuilder()
.Configure()
// ...
.WithAutofacDefaults(container)
.WithGlobalInboundInterceptorTypes(typeof(LoggingInterceptor))
.WithGlobalOutboundInterceptorTypes(typeof(MetricsInterceptor))
.Build();
Multiple Assemblies
Pass all assemblies that contain message types or handlers to AssemblyScanningTypeProvider:
var typeProvider = new AssemblyScanningTypeProvider(
typeof(PlaceOrderCommand).Assembly, // Message contracts assembly
typeof(PlaceOrderHandler).Assembly, // Handlers assembly
typeof(GetOrderRequest).Assembly, // More contracts
typeof(OrderPlacedEvent).Assembly // Events
);
If your messages and handlers are in the same assembly, you only need to reference it once. AssemblyScanningTypeProvider deduplicates assemblies automatically.
ASP.NET Core Integration
In an ASP.NET Core application, Autofac can be used as the service provider. Configure Nimbus in the ConfigureContainer method:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// other service registrations
}
public void ConfigureContainer(ContainerBuilder builder)
{
var typeProvider = new AssemblyScanningTypeProvider(
typeof(PlaceOrderHandler).Assembly
);
builder.RegisterNimbus(typeProvider);
builder.RegisterType<OrderRepository>()
.As<IOrderRepository>()
.InstancePerLifetimeScope();
// Register bus as singleton
builder.Register(context =>
{
var bus = new BusBuilder()
.Configure()
.WithTransport(new RedisTransportConfiguration()
.WithConnectionString("localhost:6379"))
.WithNames("OrderService", Environment.MachineName)
.WithAutofacDefaults(context.Resolve<IComponentContext>())
.Build();
bus.Start().GetAwaiter().GetResult();
return bus;
})
.As<IBus>()
.SingleInstance();
}
}
Disposal
Always stop and dispose the bus before the application exits:
// Console application
await bus.Stop();
bus.Dispose();
container.Dispose();
// ASP.NET Core — Autofac handles disposal automatically
// Just register the bus and it will be disposed when the container is disposed
Common Patterns
Registering Services Shared Across Handlers
Register services that all handlers use as singletons (if thread-safe) or scoped:
// Thread-safe — register as singleton
builder.RegisterType<HttpClientFactory>()
.As<IHttpClientFactory>()
.SingleInstance();
// Not thread-safe — register as scoped
builder.RegisterType<OrderDbContext>()
.AsSelf()
.InstancePerLifetimeScope();
Decorating Handlers
Autofac’s decorator support works with Nimbus handlers:
builder.RegisterType<PlaceOrderHandler>()
.Named<IHandleCommand<PlaceOrderCommand>>("inner");
builder.RegisterDecorator<IHandleCommand<PlaceOrderCommand>>(
(context, inner) => new RetryingPlaceOrderHandler(inner),
"inner");
Next Steps
- Dependency Injection architecture — how Nimbus manages handler lifetimes
- Interceptors — cross-cutting concerns via interceptors
- Testing — testing with Autofac and the InProcess transport