Skip to content

Repository files navigation

Injectio

Source generator that helps register attribute marked services in the dependency injection ServiceCollection

Build Project

Coverage Status

Injectio

Source generator

Features

  • Transient, Singleton, Scoped service registration
  • Factory registration
  • Module method registration
  • Duplicate Strategy - Skip,Replace,Append
  • Registration Strategy - Self, Implemented Interfaces, Self With Interfaces
  • Decorator registration (RegisterDecorator) — no runtime dependencies

Usage

Requirements

Requires Roslyn 4.14 or later. This means Visual Studio 2022 version 17.14+ or Visual Studio 2026+, a current Rider release, and the .NET 9.0.300 SDK or newer. Older toolchains will not load the analyzer.

Add package

Add the nuget package project to your projects.

dotnet add package Injectio

Registration Attributes

Place registration attribute on class. The class will be discovered and registered.

  • [RegisterSingleton] Marks the class as a singleton service
  • [RegisterScoped] Marks the class as a scoped service
  • [RegisterTransient] Marks the class as a transient service
  • [RegisterServices] Marks the method to be called to register services
  • [RegisterDecorator] Marks the class as a decorator around an existing service

Attribute Properties

PropertyDescription
ImplementationTypeThe type that implements the service. If not set, the class the attribute is on will be used.
ServiceTypeThe type of the service. If not set, the Registration property used to determine what is registered.
FactoryName of a factory method to create new instances of the service implementation.
DuplicateHow the generator handles duplicate registrations. See Duplicate Strategy
RegistrationHow the generator determines what to register. See Registration Strategy

Duplicate Strategy

ValueDescription
SkipSkips registrations for services that already exists
ReplaceReplaces existing service registrations
AppendAppends a new registration for existing services

Registration Strategy

ValueDescription
SelfRegisters each matching concrete type as itself
ImplementedInterfacesRegisters each matching concrete type as all of its implemented interfaces
SelfWithInterfacesRegisters each matching concrete type as all of its implemented interfaces and itself

Singleton services

[RegisterSingleton]publicclassSingletonService:IService{}

Explicit service type

[RegisterSingleton(ServiceType=typeof(IService))]publicclassSingletonService:IService{}

Support resolving multiple services with IEnumerable<T>

[RegisterSingleton(Duplicate=DuplicateStrategy.Append)]publicclassSingletonService:IService{}

Scoped Services

[RegisterScoped]publicclassScopedService:IService{}

Transient Services

[RegisterTransient]publicclassTransientService:IService{}

Factories

[RegisterTransient(Factory=nameof(ServiceFactory))]publicclassFactoryService:IFactoryService{privatereadonlyIService_service;publicFactoryService(IServiceservice){_service=service;}publicstaticIFactoryServiceServiceFactory(IServiceProviderserviceProvider){returnnewFactoryService(serviceProvider.GetService<IService>());}}

Open Generic

[RegisterSingleton(ImplementationType=typeof(OpenGeneric<>),ServiceType=typeof(IOpenGeneric<>))]publicclassOpenGeneric<T>:IOpenGeneric<T>{}

In version 5.0+, self-registration of open-generic types supported

[RegisterSingleton]publicclassOpenGeneric<T>:IOpenGeneric<T>{}

Generic Attributes

You can use generic attributes to register services if your project targets .NET 7.0+

<ProjectSdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0</TargetFrameworks>
</PropertyGroup>
</Project>

Generic attributes allow declaration to be more compact by avoiding the typeof calls

[RegisterSingleton<IService>]publicclassServiceImplementation:IService{}

Keyed Services

You can register keyed services with version 8.0+ of Microsoft.Extensions.DependencyInjection

Register a keyed service

[RegisterSingleton<IServiceKeyed>(ServiceKey="Alpha")]publicclassServiceAlphaKeyed:IServiceKeyed{}[RegisterSingleton<IServiceKeyed>(ServiceKey="Beta")]publicclassServiceBetaKeyed:IServiceKeyed{}

Register using an enum

publicenumServiceType{Alpha,Beta}[RegisterSingleton<IServiceKeyed>(ServiceKey=ServiceType.Alpha)]publicclassServiceAlphaTypeKeyed:IServiceKeyed{}[RegisterSingleton<IServiceKeyed>(ServiceKey=ServiceType.Beta)]publicclassServiceBetaTypeKeyed:IServiceKeyed{}

Register using an factory method

[RegisterSingleton<IServiceKeyed>(ServiceKey="Charlie",Factory=nameof(ServiceFactory))][RegisterSingleton<IServiceKeyed>(ServiceKey="Delta",Factory=nameof(ServiceFactory))]publicclassServiceFactoryKeyed:IServiceKeyed{publicServiceFactoryKeyed(object?serviceKey){ServiceKey=serviceKey;}publicobject?ServiceKey{get;}publicstaticIServiceKeyedServiceFactory(IServiceProviderserviceProvider,object?serviceKey){returnnewServiceFactoryKeyed(serviceKey);}}

Decorators

Use the RegisterDecorator attribute to wrap an existing service registration without adding any runtime dependencies. The generator emits all decoration helpers directly into the consumer assembly.

Decorators inherit the lifetime of the service they decorate. Apply multiple decorators by ordering them with the Order property — lower values are innermost (applied first), higher values are outermost (applied last).

publicinterfaceIService{}[RegisterSingleton<IService>]publicclassService:IService{}[RegisterDecorator<IService>(Order=1)]publicclassLoggingDecorator:IService{publicLoggingDecorator(IServiceinner){}}[RegisterDecorator<IService>(Order=2)]publicclassCachingDecorator:IService{publicCachingDecorator(IServiceinner){}}

Resolution order for the sample above: CachingDecorator → LoggingDecorator → Service.

Decorator Attribute Properties
PropertyDescription
ServiceTypeThe type of service to decorate. Required unless the generic attribute form is used.
ImplementationTypeThe decorator type. If not set, the class the attribute is on will be used.
ServiceKeyDecorate a specific keyed registration. Requires .NET 8+ Microsoft.Extensions.DependencyInjection.
AnyKeyWhen true, decorate every keyed registration of ServiceType regardless of its key.
FactoryName of a static factory method that builds the decorator.
OrderOrdering within the decoration chain. Lower = innermost.
TagsComma/semicolon-delimited list of registration tags.
Keyed decoration

Decorate a single keyed variant, or use AnyKey to decorate them all:

[RegisterSingleton<IService>(ServiceKey="alpha")]publicclassAlphaService:IService{}[RegisterDecorator<IService>(AnyKey=true)]publicclassLoggingDecorator:IService{publicLoggingDecorator(IServiceinner){}}
Factory-built decorators

Provide a static factory on the decorator class for complex construction:

[RegisterDecorator<IService>(Factory=nameof(Create))]publicclassLoggingDecorator:IService{publicLoggingDecorator(IServiceinner){}publicstaticIServiceCreate(IServiceProviderserviceProvider,IServiceinner)=>newLoggingDecorator(inner);}

For keyed decorators the factory takes an additional object? parameter for the key:

publicstaticIServiceCreate(IServiceProviderserviceProvider,object?serviceKey,IServiceinner)=>newLoggingDecorator(inner);
Open-generic decoration

Open-generic decorators apply to every closed registration of the matching service type. The generator supports decorating closed-generic registrations with an open-generic decorator class; purely open-generic implementation registrations (e.g. (IRepo<>, Repo<>)) are not decorated at runtime due to a Microsoft.Extensions.DependencyInjection limitation on factory registrations for open generic service types.

publicinterfaceIRepo<T>{}[RegisterSingleton<IRepo<string>,StringRepo>]publicclassStringRepo:IRepo<string>{}[RegisterDecorator(ServiceType=typeof(IRepo<>))]publicclassLoggingRepo<T>:IRepo<T>{publicLoggingRepo(IRepo<T>inner){}}
Tags

Decorators support the same tag-filtering as registrations:

[RegisterDecorator<IService>(Tags="FrontEnd")]publicclassFrontEndLoggingDecorator:IService{publicFrontEndLoggingDecorator(IServiceinner){}}

Register Method

When the service registration is complex, use the RegisterServices attribute on a method that has a parameter of IServiceCollection or ServiceCollection

publicclassRegistrationModule{[RegisterServices]publicstaticvoidRegister(IServiceCollectionservices){// add and bind configuration options, Microsoft.Extensions.Configuration.Binderservices.AddOptions<PollingOption>().Configure<IConfiguration>((settings,configuration)=>configuration.GetSection(PollingOption.SectionName).Bind(settings));}}

Add to container

The source generator creates an extension method with all the discovered services registered. Call the generated extension method to add the services to the container. The extension method will be called Add[AssemblyName]. The assembly name will have the dots removed.

varservices=newServiceCollection();services.AddInjectioTestsConsole();

Override the extension method name by using the InjectioName MSBuild property.

<PropertyGroup>
<InjectioName>Library</InjectioName>
</PropertyGroup>
<ItemGroup>
<CompilerVisiblePropertyInclude="InjectioName" />
</ItemGroup>
varservices=newServiceCollection();services.AddLibrary();

Registration Tags

Control what is registered when calling the generated extension method using Tags

Tag the service

publicinterfaceIServiceTag{}[RegisterSingleton(Tags="Client,FrontEnd")]publicclassServiceTag:IServiceTag{}

Tags can be passed to register methods

publicstaticclassServiceRegistration{[RegisterServices]publicstaticvoidRegister(IServiceCollectionservices,ISet<string>tags){}}

Specify tags when adding to service collection. Note, if no tags specified, all services are registered

varservices=newServiceCollection();services.AddInjectioTestsLibrary("Client");

About

Source generator that helps register attribute marked services in the dependency injection ServiceCollection

Resources

Stars

249 stars

Watchers

6 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages