Dependency Injection
Stop manually wiring up every service in your DI container. Shiny Extensions DI uses source generators to scan your attributed classes at compile time and generate all your registrations into a single file. Add an attribute, and it’s registered — no reflection, no startup cost, fully AOT-compatible.
Features
Section titled “Features”- Source-generated service registrations from attributes — zero boilerplate
[Singleton],[Scoped], and[Transient]lifetime attributes- Multiple interface registration with shared instance resolution
- Open generics support
- Keyed services support
- Category-based conditional registration — great for testing and environment switching
TryAddsemantics to avoid overwriting existing registrations- Startup tasks for post-DI initialization
- Works with classes and records
-
Install the NuGet package:
Terminal window dotnet add package Shiny.Extensions.DependencyInjection -
Add attributes to your classes:
[Singleton]public class MyService : IMyService { } -
Register all generated services during app startup:
builder.Services.AddShinyServiceRegistry();
Registration Attributes
Section titled “Registration Attributes”Mark any class with one of the three lifetime attributes:
[Singleton]public class CacheService : ICacheService { }
[Scoped]public class OrderRepository : IOrderRepository { }
[Transient]public class RequestValidator : IRequestValidator { }All three attributes support the same properties:
| Property | Type | Description |
|---|---|---|
AsSelf | bool | Register as the concrete class instead of its interface |
Type | Type | Target a specific interface when the class implements multiple |
KeyedName | string? | Register as a keyed service |
Category | string? | Tag for conditional registration |
TryAdd | bool | Use TryAdd semantics — won’t replace existing registrations |
What Gets Generated
Section titled “What Gets Generated”This:
[Singleton]public record MyStandardSingletonRecord : IStandardInterface;
[Scoped]public record MyStandardScopedRecord;
[Singleton(AsSelf = true)]public class AsSelfTest : IStandardInterface;
[Singleton(TryAdd = true)]public class StandardImplementation : IStandardInterface;
[Scoped(KeyedName = "Standard")]public class KeyedStandardImplementation : IStandardInterface;
[Singleton]public class MultipleImplementation : IStandardInterface, IStandardInterface2;
[Singleton(Category = "DEV")]public class DevCategoryService;Generates this:
// <auto-generated />using global::Microsoft.Extensions.DependencyInjection;
internal static class __ShinyServicesModule{ [global::System.Runtime.CompilerServices.ModuleInitializer] public static void Run() { global::Shiny.Extensions.DependencyInjection.Internals.ServiceRegistry.RegisterCallback((services, categories) => { services.AddSingleton<global::Sample.IStandardInterface, global::Sample.MyStandardSingletonRecord>(); services.AddScoped<global::Sample.MyStandardScopedRecord>(); services.AddSingleton<global::Sample.AsSelfTest>(); services.TryAddSingleton<global::Sample.IStandardInterface, global::Sample.StandardImplementation>(); services.AddKeyedScoped<global::Sample.IStandardInterface, global::Sample.KeyedStandardImplementation>("Standard"); global::Shiny.Extensions.DependencyInjection.ServiceCollectionExtensions.AddSingletonAsImplementedInterfaces<global::Sample.MultipleImplementation>(services); if (categories?.Any(x => x.Equals("DEV", global::System.StringComparison.OrdinalIgnoreCase)) == true) { services.AddSingleton<global::Sample.DevCategoryService>(); } }); }}