π Attribute-based automatic dependency injection library for .NET
dotnet add package AttributeAutoDI --version 1.0.1
The default DI container in ASP.NET Core is simple and powerful, but having to call builder.Services.AddXXX<>() repeatedly introduces a few pain points:
β The more services you implement, the longer and harder your Program.cs becomes to manage
β Handling multiple implementations of the same interface requires verbose, error-prone registration
β Service intent is not self-explanatory β registration and class definition are separated
π What if... classes could declare how they want to be injected? AttributeAutoDI was born from this idea.
It is built on the following principles: β Services should declare their own lifecycle and purpose
β Multiple implementations should be clearly handled via [Primary] or [Named]
β We should reduce boilerplate code in Program.cs without sacrificing readability
Our goal is to simplify Program.cs and Startup.cs as much as possible so you can trust the system to "just work."
- .NET 6.0 or later
- Compatible with Microsoft.Extensions.DependencyInjection (ASP.NET Core, Console apps, etc.)
β Automatic registration via [Singleton], [Scoped], and [Transient]
β [Primary] support for selecting default implementation
π·οΈ [Named] support for explicit injection via constructor parameters
π οΈ [Options("Section")] for automatic configuration binding
πΎ Executes configuration methods marked with [Execute] inside classes decorated with [PreConfiguration] or [PostConfiguration].
builder.Services.AddAttributeDependencyInjection(builder.Configuration);
or
builder.Services.AddAttributeDependencyInjection(builder.Configuration,typeof(Program).Assembly);
Internally, it expands to:
public static class AttributeInjectionExtension
{
public static void AddAttributeDependencyInjection(
this IServiceCollection services,
IConfiguration configuration,
Assembly? assembly = null
)
{
assembly ??= Assembly.GetEntryAssembly();
services.UsePreConfiguration(assembly!);
services.UseOptionsBindingInjection(configuration, assembly!);
services.UseAttributeInjection(assembly!);
services.UsePrimaryInjection();
services.UseNameParameterInjection(assembly!);
services.Replace(ServiceDescriptor.Transient<IControllerActivator, NamedControllerActivator>());
services.UsePostConfiguration(assembly!);
}
-
Execute methods under [PreConfiguration] classes
-
Register [Options] bindings
-
Register class dependencies marked with [Singleton], [Transient], or [Scoped]
-
Register [Primary] dependencies
-
Register constructor parameter classes and controller activators with [Named]
-
Execute methods under [PostConfiguration] classes
βοΈ This works as expected:
builder.Service.AddSingleton...
builder.Service.AddAttributeDependencyInjection
β This may not work correctly:
builder.Service.AddAttributeDependencyInjection
builder.Service.AddSingleton...
If you use both manual and attribute-based registration, always call AddAttributeDependencyInjection() after manual registrations.
[Singleton]
public class MyService : IMyService
{
public string Get() => "hello";
}
This is equivalent to:
builder.Services.AddSingleton<IMyService, MyService>();
[Singleton]
public class MyService : IMyService
{
public string Get() => "hello";
}
[Primary]
[Singleton]
public class MyPrimaryService : IMyService
{
public string Get() => "I am primary";
}
When injecting IMyService, the system will prefer MyPrimaryService.
[Singleton]
public class MyService : IMyService
{
public string Get() => "hello";
}
This will register myService (default camel-case name). Or set custom name:
[Singleton("myNamedService")]
public class MyService : IMyService
{
public string Get() => "hello";
}
Then inject with:
[Singleton]
public class TestFacade([Named("myNamedService")]IMyservice myservice)
{
...
}
// myservice == MyService
Named injection works in registered Class
[Options("Sample:Sample")]
public class SampleOption
{
public string Sample { get; set; } = "";
}
This is equivalent to:
builder.Services.Configure<SampleOption>(configuration.GetSection("Sample:Sample"));
Then you can inject it with:
public class SampleController(
IOptions<SampleOption> sampleOption) ...
You can run setup methods before or after AddAttributeDependencyInjection using [Execute] methods.
The execution order is:
PreConfiguration β AddAttributeDependencyInjection β PostConfiguration
[PreConfiguration] //or [PostConfiguration]
public static class Configuration{
[Execute]
public static void TestConfig1(IServiceCollection service){
...
}
[Execute]
public static void TestConfig2(this IServiceCollection service){
...
}
[Execute]
public static void TestConfig3(IServiceCollection service, IConfiguration configuration){
...
}
[Execute]
public static void TestConfig4(this IServiceCollection service, IConfiguration configuration){
...
}
}
β Only these four method signatures are supported.
βΉοΈ Return types are ignored β any return type is allowed but not used.
Check out the AttributeAutoDI.Sample project for practical examples.
Found a bug? Have an improvement idea? Feel free to open an issue or submit a pull request.
MIT License Copyright (c) 2025