SsalKit.DependencyInjection
0.0.1
See the version list below for details.
dotnet add package SsalKit.DependencyInjection --version 0.0.1
NuGet\Install-Package SsalKit.DependencyInjection -Version 0.0.1
<PackageReference Include="SsalKit.DependencyInjection" Version="0.0.1" />
<PackageVersion Include="SsalKit.DependencyInjection" Version="0.0.1" />
<PackageReference Include="SsalKit.DependencyInjection" />
paket add SsalKit.DependencyInjection --version 0.0.1
#r "nuget: SsalKit.DependencyInjection, 0.0.1"
#:package SsalKit.DependencyInjection@0.0.1
#addin nuget:?package=SsalKit.DependencyInjection&version=0.0.1
#tool nuget:?package=SsalKit.DependencyInjection&version=0.0.1
SsalKit.DependencyInjection
Compile-time DI auto-registration for .NET, powered by a Roslyn source generator — no reflection, no assembly scanning, no runtime cost.
Why SsalKit.DependencyInjection?
Most "automatic" DI registration libraries work by scanning loaded assemblies with reflection at startup. That approach has real costs: startup latency that grows with your assembly count, metadata that trimming and Native AOT can't reason about, and failures that only surface at runtime.
SsalKit.DependencyInjection takes a different approach:
- Compile-time, not runtime. A source generator inspects your code during the build and emits the registration calls directly as C# — there is no scanning step to run when your app starts.
- Zero reflection. The generated code is a plain, readable
IServiceCollectionextension method. Nothing is discovered dynamically. - AOT & trimming friendly. Because registrations are ordinary generated code rather than reflection-driven discovery, they survive trimming and Native AOT compilation without extra annotations.
- Compile-time diagnostics. Mistakes like registering an abstract class or an unimplemented interface are caught by the compiler, not by a runtime exception in production.
Installation
dotnet add package SsalKit.DependencyInjection
The package contains both the [Service] attribute and the source generator — no separate analyzer package to install.
Quick Start
Decorate the classes you want registered:
using Microsoft.Extensions.DependencyInjection;
using SsalKit.DependencyInjection;
// Registered under every interface it implements (or itself, if it implements none).
[Service(ServiceLifetime.Singleton)]
public class CacheService : ICacheService { }
// Registered only as the given type.
[Service(ServiceLifetime.Scoped, As = typeof(IUserRepository))]
public class UserRepository : IUserRepository, IDisposable { }
// Defaults to ServiceLifetime.Singleton.
[Service]
public class EmailSender { }
// Registered as a keyed service (.NET 8+ keyed DI).
[Service(ServiceLifetime.Singleton, Key = "redis")]
public class RedisCache : ICache { }
// Only registered if IClock hasn't already been registered.
[Service(ServiceLifetime.Singleton, Mode = RegistrationMode.TryAdd)]
public class DefaultClock : IClock { }
At build time, the generator emits one extension method per assembly, named after the assembly itself. For example, an assembly called MyApp.Web gets AddMyAppWebServices(), roughly equivalent to:
// <auto-generated/>
namespace Microsoft.Extensions.DependencyInjection;
public static class MyAppWebServiceCollectionExtensions
{
public static IServiceCollection AddMyAppWebServices(this IServiceCollection services)
{
services.AddSingleton<CacheService>();
services.AddSingleton<ICacheService>(sp => sp.GetRequiredService<CacheService>());
services.AddScoped<IUserRepository, UserRepository>();
services.AddSingleton<EmailSender>();
services.AddKeyedSingleton<ICache, RedisCache>("redis");
services.TryAddSingleton<IClock, DefaultClock>();
return services;
}
}
Wire it up in Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMyAppWebServices();
var app = builder.Build();
Multi-interface
Singleton/Scopedregistrations are generated as a concrete registration plus factory forwarding, so every interface resolves to the same shared instance.
Attribute Reference
[Service(lifetime, As = ..., Mode = ..., Key = ...)]
| Property | Type | Default | Description |
|---|---|---|---|
Lifetime |
ServiceLifetime |
ServiceLifetime.Singleton |
Constructor argument. Singleton, Scoped, or Transient. |
As |
Type? |
null |
Registers only as this type instead of every implemented interface (or itself). |
Mode |
RegistrationMode |
RegistrationMode.Add |
Add, TryAdd, TryAddEnumerable, or Replace — how the call is applied to the collection. |
Key |
object? |
null |
Registers as a keyed service (.NET 8+) using AddKeyed* instead of Add*. |
A class can carry multiple [Service] attributes to register it several ways (different lifetimes, As targets, or keys) at once.
Diagnostics
The generator validates your [Service] usage at compile time:
| ID | Severity | Description |
|---|---|---|
SSAL001 |
Error | [Service] was applied to an abstract or static class. |
SSAL002 |
Error | The type specified in As is not implemented by the decorated class. |
SSAL003 |
Error | Open generic types are not supported. |
SSAL004 |
Warning | The same service registration appears to be duplicated. |
SSAL005 |
Error | Key was combined with RegistrationMode.TryAddEnumerable, which is not a supported combination. |
License
MIT — see LICENSE.
AI disclosure: This project was built with AI assistance (Claude).
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.