Skip to main content

🧩 Introduction to GenDI

CI/CD Pipeline Deploy Documentation NuGet GenDI NuGet GenDI.SourceGenerator License: MIT

GenDI is an attribute-first dependency injection source generator for .NET. It generates DI registrations and activation code at compile time β€” no reflection, no runtime scanning, no boilerplate.

😀 Stop writing constructor boilerplate​

Every .NET developer knows the pain: a service gains one more dependency, so you update the constructor signature, add another private field, and repeat the same assignment yet again. As a codebase grows, constructors become maintenance burdens rather than meaningful code.

GenDI eliminates this ceremony entirely with property injection:

// ❌ Traditional constructor injection β€” repetitive, noisy, hard to extend
public class ReportService
{
private readonly IReportRepository _repo;
private readonly IEmailService _email;
private readonly IStorageService _storage;
private readonly ILogger<ReportService> _logger;

public ReportService(
IReportRepository repo,
IEmailService email,
IStorageService storage,
ILogger<ReportService> logger)
{
_repo = repo;
_email = email;
_storage = storage;
_logger = logger;
}
}
// βœ… GenDI property injection β€” clean, declarative, self-documenting
[Injectable<IReportService>(ServiceLifetime.Scoped)]
public class ReportService : IReportService
{
[Inject] public required IReportRepository Repo { get; init; }
[Inject] public required IEmailService Email { get; init; }
[Inject] public required IStorageService Storage { get; init; }
[Inject] public required ILogger<ReportService> Logger { get; init; }
}

The required keyword guarantees that every dependency is provided β€” the compiler enforces it. GenDI generates the wiring code so you never touch it.

πŸ† Why property injection wins​

Constructor injectionGenDI property injection
βž• Adding a dependencyEdit ctor signature + field + assignmentAdd one [Inject] property
πŸ‘€ Reading the classCtor signature + private fieldsProperties listed at a glance
πŸ§ͺ Unit testingBuild a mock for every ctor paramAssign only the props you need
πŸ”€ RefactoringRisk of parameter order mistakesProperties are named β€” no position bugs
βš™οΈ Generated codeNoneExplicit, readable, debuggable

✨ Key features and practical value​

  • 🎯 Property injection as first-class citizen: [Inject] on required init-only properties β€” dependencies read like documentation, not plumbing.
  • 🚫 Zero boilerplate registration: one [Injectable] attribute replaces manual AddScoped<>() calls in startup files.
  • πŸ”’ Compile-time safety: the C# compiler enforces that every required [Inject] property is assigned in the generated initializer β€” you cannot accidentally omit a dependency. Note: unregistered services still surface as runtime container exceptions, just like standard DI.
  • πŸ“– Readable generated flow: activation uses explicit new + GetRequiredService<T>(), easy to inspect and debug.
  • πŸ“ Predictable behavior: deterministic ordering with Group and Order avoids ambiguous pipeline composition.
  • πŸ”‘ Modern DI scenarios: keyed registrations and keyed resolution through both Microsoft DI and GenDI attributes.
  • ⚑ No startup overhead: compile-time generation eliminates reflection-based scanning costs.
  • πŸš€ Future-proof deployment: NativeAOT and trimming support when you need it β€” no friction for traditional deployments.

πŸ’‘ Why GenDI exists​

Traditional runtime scanning is practical, but adds startup cost and can break with aggressive trimming or NativeAOT. GenDI shifts all of this to compile-time:

  • Registration mapping is generated from attributes.
  • Property and constructor injection are generated as strongly typed C# code.
  • Microsoft DI remains the runtime container β€” GenDI is purely additive.

πŸ“‹ Feature summary​

  • [Injectable] and [Injectable<TService>] to mark concrete services
  • [ServiceInjection] to mark interfaces/abstract contracts
  • [Inject] for init-only property injection (get; init;) β€” the idiomatic GenDI way
  • [assembly: GenDICoveration(...)] to control generated extension coverage behavior
  • Ordering by Group, then Order, then service type name (ordinal)

βš™οΈ Typical generation output​

GenDI generates an extension method in the consumer assembly namespace:

// <AssemblyName>.DependencyInjection
services.AddGenDIServices();

Each registration uses generated new expressions and GetRequiredService<T>(), keeping activation explicit and analyzer-friendly.

πŸ—ΊοΈ Documentation map​

  • πŸ“¦ Getting Started: installation and first setup
  • πŸ“š Core Concepts: attributes, contracts, registration strategy
  • πŸ”¬ Advanced: NativeAOT/trimming validation, benchmarks and test strategy
  • 🌍 Community: contribution, roadmap and sponsorship