๐ 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 == MyServiceNamed 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