GenDI.SourceGenerator
2026.6.2.765
dotnet add package GenDI.SourceGenerator --version 2026.6.2.765
NuGet\Install-Package GenDI.SourceGenerator -Version 2026.6.2.765
<PackageReference Include="GenDI.SourceGenerator" Version="2026.6.2.765"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="GenDI.SourceGenerator" Version="2026.6.2.765" />
<PackageReference Include="GenDI.SourceGenerator"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add GenDI.SourceGenerator --version 2026.6.2.765
#r "nuget: GenDI.SourceGenerator, 2026.6.2.765"
#:package GenDI.SourceGenerator@2026.6.2.765
#addin nuget:?package=GenDI.SourceGenerator&version=2026.6.2.765
#tool nuget:?package=GenDI.SourceGenerator&version=2026.6.2.765
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. - Registration strategy control:
RegistrationMultiplicity+RegistrationEmissionStrategylet you chooseSingle/MultipleandAdd/TryAddgeneration semantics. - Options mapping evolution:
[OptionConfig]supports optional key fallback (type name) plus optimizedAddOptions<T>().BindConfiguration(section)registration. - Testing ergonomics:
GenDI.Testingincludes a fluentServiceBuilderfor xUnit/unit-test composition. - 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
dotnet add package GenDI.Testing
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(); // if using decorators, call AddGenDIServices() after all other registrations to ensure correct ordering
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>] or let GenDI infer the
[ServiceInjection] contract with non-generic [DecoratorFor(Order = ...)]:
[Injectable<IMyService>(ServiceLifetime.Singleton)]
public sealed class CoreService : IMyService
{
public void Execute() { }
}
[DecoratorFor<IMyService>(Order = 0)]
public sealed class LoggingDecorator(IMyService inner) : IMyService
{
public void Execute() => inner.Execute();
}
[DecoratorFor(Order = 1)]
public sealed class ValidationDecorator : IMyService
{
[Inject]
public required IMyService Inner { get; init; }
public void Execute() => Inner.Execute();
}
Decorator pipelines are emitted statically in ascending Order; ties fall back to the decorator
type name using ordinal comparison. Decorators must expose a public constructor parameter or
[Inject] init-only property matching the decorated service contract.
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)GENDI004— non-generic[DecoratorFor]must resolve exactly one closed[ServiceInjection]contractGENDI005— decorators must expose the decorated contract as a constructor parameter or[Inject]property
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 | Status |
|---|---|
| .NET 8+ | YES |
| NativeAOT | YES |
| Trimming | YES |
| Microsoft.Extensions.DependencyInjection | YES |
| ASP.NET Core Minimal API | YES |
| Worker Service / hosted services | YES |
| Blazor WebAssembly | YES |
| MAUI / mobile AOT | Manual validation recipe |
| F# | Exploration only (no generated AddGenDIServices()) |
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.
Phase 6 delivery baseline (single status source)
The canonical track status is maintained in /home/runner/work/GenDI/GenDI/docs/ROTEIRO_FASE6.md and mirrored here:
| Track | Status |
|---|---|
| 4.1 Source-generator quality | Delivered |
| 4.2 Registration model (RM-01..RM-12) | Delivered |
| 4.3 Platform/framework support | Delivered |
| 4.4 Testing ergonomics | Delivered |
| 4.5 Explicit registration strategies (Add/TryAdd) | Delivered |
| 4.6 OptionConfig evolution | Delivered |
| 4.7 Tooling/IDE | Pending |
| 4.8 Observability | Pending |
| 4.9 Community/ecosystem | Pending |
Detailed references:
Detailed RM-01..RM-12 documentation:
Platform/framework notes:
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
- GenDI (>= 2026.6.2.765)
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 | 78 | 6/2/2026 |
| 2026.5.25.1004 | 726 | 5/25/2026 |
| 2026.5.19.1218 | 529 | 5/19/2026 |
| 2026.5.19.867 | 101 | 5/19/2026 |
| 2026.5.19.149 | 96 | 5/19/2026 |
| 2026.5.18.1231 | 96 | 5/18/2026 |
| 2026.5.18.1180 | 94 | 5/18/2026 |
| 2026.5.18.894 | 100 | 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 | 158 | 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 | 118 | 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 |