๐ท๏ธ Attribute Reference
InjectableAttributeโ
Marks a concrete class for generation-based registration.
[Injectable(ServiceLifetime.Transient)]
public sealed class ConcreteService { }
โ๏ธ Key membersโ
- โฑ๏ธ
Lifetime: DI lifetime (Singleton,Scoped,Transient) - ๐
Group: primary ordering key (defaultint.MaxValue) - ๐ข
Order: secondary ordering key inside group (defaultint.MaxValue) - ๐
Key: optional keyed-service identifier (defaultnull)
ServiceType in non-generic form is always null.
InjectableAttribute<TService>โ
Use when you want an explicit contract through a closed generic argument.
[Injectable<IMyService>(ServiceLifetime.Singleton)]
public sealed class MyService : IMyService { }
ServiceType is automatically derived as typeof(TService).
ServiceInjectionAttributeโ
Marks interfaces or abstract types that should be discoverable as injectable contracts while GenDI traverses implementations and inheritance.
[ServiceInjection]
public interface IMyService { }
InjectAttributeโ
๐ [Inject] is the heart of GenDI's property injection model. Mark any required init-only property with it and GenDI generates the resolution code at compile time โ no constructor, no private field, no assignment boilerplate.
๐ค Why property injection?โ
Every constructor parameter forces three lines of code: the parameter itself, a private backing field, and an assignment. As services grow, constructors become the loudest part of a file. Property injection collapses all three into one declarative line:
// โ Without property injection โ 3 lines of ceremony per dependency
private readonly IOrderRepository _repo;
public MyService(IOrderRepository repo) { _repo = repo; }
// โ
With [Inject] โ 1 declarative line, zero private fields
[Inject] public required IOrderRepository Repo { get; init; }
The required modifier is enforced by the C# compiler: GenDI cannot generate an instance without providing every [Inject] property. This gives you the same compile-time guarantee as constructor injection โ you cannot accidentally skip a dependency in the generated initializer. Note: if a service is not registered in the DI container, GetRequiredService<T>() will still throw at runtime, just like standard DI.
๐ง Basic usageโ
[Inject]
public required IOtherService OtherService { get; init; }
๐ Keyed property injectionโ
[Inject(Key = "primary")]
public required IOtherService OtherService { get; init; }
Constructor parameters can use native DI keyed resolution:
public Consumer([FromKeyedServices("primary")] IOtherService otherService) { }
๐ Benefits at a glanceโ
| Scenario | Constructor injection | [Inject] property injection |
|---|---|---|
| โ Add dependency | Edit ctor + field + assignment | Add one property |
| ๐ Read class structure | Scan ctor + private fields | Properties visible immediately |
| ๐งช Unit test partial wiring | Must satisfy full ctor | Assign only needed properties |
| ๐ Avoid parameter order bugs | Position-sensitive | Named โ no ordering risk |
โ Requirementsโ
- Property must be
get; init; - Property must be
publicorinternal
GenDICoverationAttributeโ
Assembly-level toggle for generated extension coverage behavior.
[assembly: GenDI.GenDICoveration(false)]
- โ
true=> include generated extension in coverage - โ
false=> append[ExcludeFromCodeCoverage]to generated extension