GenDI.SourceGenerator
26.5.13.1053
See the version list below for details.
dotnet add package GenDI.SourceGenerator --version 26.5.13.1053
NuGet\Install-Package GenDI.SourceGenerator -Version 26.5.13.1053
<PackageReference Include="GenDI.SourceGenerator" Version="26.5.13.1053"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="GenDI.SourceGenerator" Version="26.5.13.1053" />
<PackageReference Include="GenDI.SourceGenerator"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add GenDI.SourceGenerator --version 26.5.13.1053
#r "nuget: GenDI.SourceGenerator, 26.5.13.1053"
#:package GenDI.SourceGenerator@26.5.13.1053
#addin nuget:?package=GenDI.SourceGenerator&version=26.5.13.1053
#tool nuget:?package=GenDI.SourceGenerator&version=26.5.13.1053
GenDI
Generator-based Dependency Injection for .NET — elegant, fast, AOT-ready
GenDI is a dependency injection library built on top of C# source generators, providing full compatibility with NativeAOT and trimming. It works as an additional module to Microsoft.Extensions.DependencyInjection, allowing you to register services automatically at compile time — no reflection required.
Say goodbye to constructor bloat
Real-world services accumulate dependencies. With traditional constructor injection this tends to look like this:
// ❌ The "constructor tax" — grows every time a new dependency is added
public class OrderProcessor
{
private readonly IOrderRepository _orderRepository;
private readonly IPaymentGateway _paymentGateway;
private readonly IEmailService _emailService;
private readonly IInventoryService _inventoryService;
private readonly ILogger<OrderProcessor> _logger;
public OrderProcessor(
IOrderRepository orderRepository,
IPaymentGateway paymentGateway,
IEmailService emailService,
IInventoryService inventoryService,
ILogger<OrderProcessor> logger)
{
_orderRepository = orderRepository;
_paymentGateway = paymentGateway;
_emailService = emailService;
_inventoryService = inventoryService;
_logger = logger;
}
}
With GenDI's property injection, the same class becomes clean and self-documenting:
// ✅ Declare what you need — GenDI wires everything at compile time
[Injectable<IOrderProcessor>(ServiceLifetime.Scoped)]
public class OrderProcessor : IOrderProcessor
{
[Inject] public required IOrderRepository OrderRepository { get; init; }
[Inject] public required IPaymentGateway PaymentGateway { get; init; }
[Inject] public required IEmailService EmailService { get; init; }
[Inject] public required IInventoryService InventoryService { get; init; }
[Inject] public required ILogger<OrderProcessor> Logger { get; init; }
}
No private fields. No constructor ceremony. No manual wiring. Just declare your dependencies and move on.
Key features and developer benefits
- Property injection as first-class citizen: use
[Inject]onrequiredinit-only properties — dependencies read like documentation, not plumbing. - Zero boilerplate registration: a single
[Injectable]attribute replacesAddScoped<TImpl>()calls scattered across startup files. - Readable generated flow: activation is emitted as explicit
new+GetRequiredService<T>(), making the wiring transparent and debuggable. - Compile-time safety: the C# compiler enforces every
required[Inject]property is assigned — you cannot accidentally skip a dependency. Container registration errors (unregistered services) remain runtime exceptions, just like standard DI. - Deterministic registration order:
Group+Ordergive you predictable, testable pipeline composition. - Attribute-first contract mapping: combine
[Injectable],[Injectable<TService>], and[ServiceInjection]with clear intent. - Keyed services support: works with both native
[FromKeyedServices]and GenDI[Inject(Key = ...)]. - Factory-first registration: use
[InjectableFactory<TService>]on static methods when construction should be centralized. - Module filtering: group registrations with
[InjectableModule]/Moduleand load only selected modules. - Options mapping:
[OptionConfig("Path")]enables automaticIOptions<T>registration from configuration. - Open-generic safety: open-generic registrations are bypassed and reported as generator warnings (
GENDISG001). - No runtime scanning cost: compile-time generation eliminates startup overhead from reflection-based scanning.
- AOT/trimming friendly by design: safe path for teams that need NativeAOT, without forcing this concern for every project.
Installation
dotnet add package GenDI
dotnet add package GenDI.SourceGenerator
GenDI.SourceGenerator now bundles GenDI.Analyzers and ships buildTransitive/GenDI.SourceGenerator.props (Using Include="GenDI").
GenDI remains the runtime package (no buildTransitive content).
When using GenDI.SourceGenerator, you normally should not install GenDI.Analyzers separately to avoid duplicate diagnostics/code-fix hints.
Usage
Using InjectableAttribute
[ServiceInjection]
public interface IMyService
{
void Execute();
}
[Injectable(ServiceLifetime.Singleton, Group = 10, Order = 1)]
public class MyService : IMyService
{
public void Execute() => Console.WriteLine("Service injected!");
}
ServiceInjectionAttribute also supports an optional fallback lifetime:
[ServiceInjection(ServiceLifetime.Scoped)]
public interface IScopedContract
{
}
Thread isolation fallback can also be configured at contract level:
[ServiceInjection(ServiceLifetime.Scoped, ThreadIsolation = ThreadIsolationPolicy.Singleton)]
public interface IThreadIsolatedContract
{
}
Fallback precedence is: Injectable > ServiceInjection > Transient.
InjectableAttribute supports:
Lifetime(constructor argument, defaultTransient)Group(optional, defaultint.MaxValue)Order(optional, defaultint.MaxValue)ServiceType:[Injectable]→null(no explicit contract)[Injectable<TService>]→typeof(TService)as explicit contract (additive with[ServiceInjection])
Key(optional, defaultnull) for keyed service registration generationThreadIsolation(optional) usingThreadIsolationPolicy.{Singleton|Scoped|Transient}for thread-local resolution cacheModule(optional) to associate registration with a module group
Service registration emission order is:
GroupOrder- Service type name (ordinal)
Registering Services
using YourProject.DependencyInjection; // generated by GenDI in the consumer project namespace
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGenDIServices();
var app = builder.Build();
app.Run();
Property Injection with [Inject]
Declare dependencies as required init-only properties and mark them with [Inject]. GenDI generates the activation code — no constructor needed:
[Injectable<IOrderProcessor>(ServiceLifetime.Scoped)]
public class OrderProcessor : IOrderProcessor
{
[Inject] public required IOrderRepository Repository { get; init; }
[Inject] public required IPaymentGateway Payment { get; init; }
[Inject] public required ILogger<OrderProcessor> Logger { get; init; }
public async Task ProcessAsync(Order order)
{
Logger.LogInformation("Processing order {Id}", order.Id);
await Repository.SaveAsync(order);
await Payment.ChargeAsync(order);
}
}
[Inject] also supports optional Key for keyed dependency resolution:
[Inject(Key = "primary")]
public required IMyService Service { get; init; }
[Inject] also supports lifetime override for indirect registration discovery:
[Inject(ServiceLifetime.Scoped)]
public required IMyService Service { get; init; }
Precedence for indirect registration lifetime is:
Inject > Injectable > ServiceInjection > Transient (tie-break favors Scoped > Singleton > Transient).
For optional dependencies that should not throw when unregistered, use [InjectOptional]:
[InjectOptional]
public required IMyService? OptionalService { get; init; }
For environment-conditional registration, combine [Injectable] with [ConditionalInjectable]:
[Injectable<IMyService>(ServiceLifetime.Singleton)]
[ConditionalInjectable("Development")]
public sealed class DevOnlyService : IMyService { }
For decorators, mark the wrapper with [DecoratorFor<TService>]:
[Injectable<IMyService>(ServiceLifetime.Singleton)]
public sealed class CoreService : IMyService { }
[DecoratorFor<IMyService>]
public sealed class LoggingDecorator(IMyService inner) : IMyService { }
For factory registration, annotate static factory methods:
[InjectableModule("Billing")]
public static class BillingFactories
{
[InjectableFactory<IMyService>(ServiceLifetime.Singleton)]
public static IMyService Create() => new MyService();
}
⚠️
[InjectableFactory<TService>]supports only closed-generic types. Open-generic return types, parameters, generic factory methods, or generic containing types are ignored and emitted as warnings.
To bind options automatically:
[OptionConfig("Features:MyOptions")]
public sealed class MyOptions
{
public string? Name { get; init; }
}
Constructor injection is also supported and can use the native DI attribute:
public MyConsumer([FromKeyedServices("primary")] IMyService service) { }
Analyzer diagnostics (GenDI.Analyzers)
GenDI.Analyzers currently publishes:
GENDI001—[Inject]requiresinit-only propertyGENDI002—[Injectable]requires concrete non-abstract classGENDI003— constructor injection can be converted to GenDI property injection (code-fix available)
Official diagnostics list:
Service Contract Discovery
- GenDI discovers services from
[ServiceInjection]in implemented interfaces and base types. Injectable<TService>is also added to the generated registration list when provided.- If no
[ServiceInjection]is found in the inheritance/implementation chain, the concrete class is registered as its own service.
Generated Coverage Configuration
By default, generated extensions are included in coverage (no [ExcludeFromCodeCoverage]).
You can control this per assembly:
[assembly: GenDI.GenDICoveration(false)] // add [ExcludeFromCodeCoverage] to generated extension
NativeAOT and Trimming (Phase 3)
GenDI includes linker descriptors and validation projects for trimming and NativeAOT scenarios.
Publish with trimming
dotnet publish tests/GenDI.Phase3.TrimValidation.App/GenDI.Phase3.TrimValidation.App.csproj -c Release
Publish with NativeAOT
dotnet publish tests/GenDI.Phase3.NativeAotValidation.App/GenDI.Phase3.NativeAotValidation.App.csproj -c Release -r linux-x64
ILLink descriptor sample
<linker>
<assembly fullname="YourAssemblyName">
<type fullname="YourAssemblyName.DependencyInjection.GenDIServiceCollectionExtensions" preserve="all" />
</assembly>
</linker>
Documentation Website (Phase 4)
GenDI now ships an English-first Docusaurus documentation website under website/, with a theme aligned to net-mediate.
Local docs development
cd website
npm ci
npm run start
Production docs build
cd website
npm run build
GitHub Pages deployment is handled by .github/workflows/deploy-docs.yml.
Benchmarks (Phase 4)
GenDI now includes a dedicated BenchmarkDotNet project:
tests/GenDI.Benchmarks
Primary benchmark focus is startup registration cost:
- generated registration (
AddGenDIServices) - reflection-based runtime scanning
Latest published benchmark report:
docs/BENCHMARKS.md
Packaging and CI/CD Baseline (Phase 4 / early Phase 5)
The repository includes:
versions.propsfor centralized dynamic versioningpack.propsfor package metadata and packing defaults.github/workflows/ci-cd.ymland.github/workflows/auto-publish.ymlprepared for Sonar/NuGet flows
Local Tooling and Git Hooks
The repository uses local tools and Husky hooks:
dotnet-tools.jsonincludescsharpierandhusky- pre-commit runs:
dotnet csharpier format .dotnet test
For fresh clones, src/GenDI/GenDI.csproj runs a pre-restore target that executes dotnet tool restore and dotnet husky install.
Compatibility
| Platform / Framework | Supported |
|---|---|
| .NET 8+ | YES |
| NativeAOT | YES |
| Trimming | YES |
| Microsoft.Extensions.DependencyInjection | YES |
Roadmap
| Phase | Description | Status |
|---|---|---|
| 1 | InjectableAttribute - attribute-based registration |
Implemented |
| 2 | Attribute model + contract discovery + ordering | Implemented |
| 3 | Advanced NativeAOT support (ILLink.xml, trimming, AOT) | Implemented |
| 4 | Benchmarks, website/docs, and CI hardening | Implemented |
| 5 | Official NuGet publication | Implemented |
| 6 | Developer experience and ecosystem expansion | In Progress |
See the full plan in ROADMAP.md.
Detailed RM-01..RM-12 documentation:
Contributing
Contributions are welcome! Please read CONTRIBUTING.md before opening a pull request.
License
This project is licensed under the MIT License - see LICENSE.md for details.
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- No dependencies.
NuGet packages (3)
Showing the top 3 NuGet packages that depend on GenDI.SourceGenerator:
| Package | Downloads |
|---|---|
|
NetMediate
Mediator alternative for MediatR. |
|
|
NetMediate.SourceGeneration
Source generator package for NetMediate. Installs the generator directly and wires NetMediate runtime + GenDI.SourceGenerator through buildTransitive. |
|
|
NetMediate.Quartz
Quartz.NET-backed persistent notification scheduler for NetMediate. Enables crash recovery and cluster-distributed notification execution. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2026.6.2.765 | 102 | 6/2/2026 |
| 2026.5.25.1004 | 789 | 5/25/2026 |
| 2026.5.19.1218 | 529 | 5/19/2026 |
| 2026.5.19.867 | 102 | 5/19/2026 |
| 2026.5.19.149 | 97 | 5/19/2026 |
| 2026.5.18.1231 | 96 | 5/18/2026 |
| 2026.5.18.1180 | 94 | 5/18/2026 |
| 2026.5.18.894 | 101 | 5/18/2026 |
| 2026.5.17.848 | 142 | 5/17/2026 |
| 2026.5.17.753 | 95 | 5/17/2026 |
| 26.5.17.123 | 112 | 5/17/2026 |
| 26.5.16.1332 | 97 | 5/16/2026 |
| 26.5.16.989 | 160 | 5/16/2026 |
| 26.5.16.968 | 98 | 5/16/2026 |
| 26.5.16.899 | 94 | 5/16/2026 |
| 26.5.16.206 | 160 | 5/16/2026 |
| 26.5.15.1391 | 119 | 5/15/2026 |
| 26.5.14.661 | 301 | 5/14/2026 |
| 26.5.13.1053 | 130 | 5/13/2026 |
| 26.5.12.1224 | 148 | 5/12/2026 |