Bring Your Own DI Container

Use a dependency-injection container other than the two Benzene ships adapters for (Microsoft.Extensions.DependencyInjection and Autofac) — without any Benzene-specific package.

Problem statement

Your team standardises on a particular DI container — Lamar, DryIoc, Grace, LightInject, Castle Windsor, Simple Injector — and you want Benzene's handlers and middleware to register and resolve through that container, not Microsoft's built-in one.

You don't need a Benzene.<Container> adapter for this. Benzene resolves services through your host's IServiceProvider, and every one of those containers ships a Microsoft.Extensions.DependencyInjection (MS DI) integration that is an IServiceProvider. Plug the container into your host the standard .NET way and Benzene uses it unchanged.

How it works

Benzene's MicrosoftServiceResolverFactory has a constructor that takes an already-built IServiceProvider and does not own it. The host transports build it from the application's provider:

So when you replace the host's provider factory with your container's — the standard IHostBuilder.UseServiceProviderFactory(...) hook — ApplicationServices is your container's provider, and Benzene resolves every service through it. No Benzene code changes; no new package.

 your container  ──MS DI integration──▶  IServiceProvider  ──▶  Benzene (MicrosoftServiceResolverFactory)
 (Lamar / DryIoc / …)                    (the host's provider)      resolves handlers + middleware here

Prerequisites

Step-by-step (ASP.NET Core host)

Registration is unchanged — you still call UsingBenzene/AddBenzene against IServiceCollection. The only new line swaps the provider factory on the host.

Lamar

Lamar implements MS DI natively, so it's the cleanest drop-in:

using Lamar.Microsoft.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseLamar();          // ← Lamar builds the IServiceProvider from now on

builder.UseBenzene<StartUp>();    // your usual Benzene startup; ConfigureServices still uses IServiceCollection

var app = builder.Build();
app.UseBenzene();
app.Run();

DryIoc

using DryIoc;
using DryIoc.Microsoft.DependencyInjection;

builder.Host.UseServiceProviderFactory(
    // WithMicrosoftDependencyInjectionRules is required — see "Container requirements" below.
    new DryIocServiceProviderFactory(new Container(rules => rules.WithMicrosoftDependencyInjectionRules())));

Autofac (via its MS DI integration)

using Autofac.Extensions.DependencyInjection;

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());

Autofac is the one container Benzene also ships a native adapter for (Benzene.Autofac). Prefer the native adapter only if you configure Autofac through its own ContainerBuilder/modules; if you register everything through IServiceCollection, the provider path above is simpler.

That's the whole recipe. Because ConfigureServices still populates an IServiceCollection, all your existing Benzene registrations (AddBenzene, AddMessageHandlers, UseHttp, clients, health checks) are untouched.

Container requirements

Benzene registers services the way MS DI behaves, so your container must apply the same two semantics. Every container's MS DI integration applies them by design (that's what "MS DI conformance" means) — this only bites if you hand-build a raw container:

  1. Last-registration wins for a single resolve. Benzene re-registers services to override defaults (AddSingleton, TryAdd-then-override). A container that instead throws on multiple default registrations will fail at startup.
  2. Greediest resolvable constructor. Benzene has types with more than one public constructor (e.g. its JsonSerializer: .ctor() and .ctor(JsonSerializerOptions)). The container must pick the constructor whose arguments it can satisfy, as MS DI and Autofac do.

For DryIoc specifically, both come from WithMicrosoftDependencyInjectionRules() (shown above) — a raw new Container() has neither and will throw Error.UnableToSelectSinglePublicConstructorFromMultiple.

Testing

Prove it end-to-end by building the provider from your container and running a message through a real Benzene app — no host required:

var services = new ServiceCollection();
services.UsingBenzene(x =>
{
    x.AddBenzene();
    x.AddMessageHandlers(typeof(MyHandler).Assembly);
});

// Build the IServiceProvider with your container instead of Microsoft's:
var container = new Container(rules => rules.WithMicrosoftDependencyInjectionRules());
IServiceProvider provider = container.WithDependencyInjectionAdapter(services);

// Drive Benzene through it via the existing factory — no Benzene changes:
using var factory = new MicrosoftServiceResolverFactory(provider);
var response = await app.HandleAsync(request, factory);

Troubleshooting

Variations

Further reading