Skip to content

DependencyModulesDependency injection, decided at compile time

Declare registration next to the class it belongs to, and a source generator writes the IServiceCollection calls during the build. Nothing reflects, nothing scans at startup, and the trimmer can follow every registration you declared.

Declarations on the left becoming generated registration code on the right

The problem

Every .NET application keeps a list like this, and nothing checks that it is complete:

csharp
services.AddScoped<IOrderRepository, OrderRepository>();
services.AddSingleton<IEmailSender, SmtpEmailSender>();
// … another two hundred lines

Forget a line and you find out at run time, in the environment you deployed to. Reach for a runtime scanner instead and you trade that for three new problems: you can no longer read what was registered, the scan runs on every start, and the trimmer cannot see through reflection — so a published, trimmed build registers nothing at all.

What it looks like instead

Mark the class, and the registration is written for you during the build.

csharp
[SingletonService]
public class SmtpEmailSender : IEmailSender { }

[DependencyModule]
public partial class ApplicationModule;
csharp
var services = new ServiceCollection();

services.AddModule<ApplicationModule>();

Or declare a rule once, and let it cover everything that fits — including the handler somebody adds next year.

csharp
[DependencyModule]
public partial class HandlerModule : IConventionModule {
    void IConventionModule.Conventions(IConventionDefinitions conventions) {
        conventions.RegisterAll(typeof(IRequestHandler<,>)).AsScoped();
        conventions.RegisterAll<IValidator>().InNamespaceOf<OrderMarker>().AsScoped();
    }
}

That body never runs. It is read during the build, and what comes out the other side is the same registration code you would have written by hand:

csharp
services.AddScoped(typeof(IRequestHandler<CreateOrder, OrderId>), typeof(CreateOrderHandler));
services.AddScoped(typeof(IRequestHandler<RenameOrder, OrderId>), typeof(RenameOrderHandler));

Released under the MIT License.