Skip to content

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.

  • GitHub stars for shinyorg/extensions
  • 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
  • TryAdd semantics to avoid overwriting existing registrations
  • Startup tasks for post-DI initialization
  • Works with classes and records
  1. Install the NuGet package:

    Terminal window
    dotnet add package Shiny.Extensions.DependencyInjection
  2. Add attributes to your classes:

    [Singleton]
    public class MyService : IMyService { }
  3. Register all generated services during app startup:

    builder.Services.AddShinyServiceRegistry();

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:

PropertyTypeDescription
AsSelfboolRegister as the concrete class instead of its interface
TypeTypeTarget a specific interface when the class implements multiple
KeyedNamestring?Register as a keyed service
Categorystring?Tag for conditional registration
TryAddboolUse TryAdd semantics — won’t replace existing registrations

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>();
}
});
}
}