Keyed Services
SourceGen.Ioc supports keyed service registration with Key and KeyType.
Support keyed service injecting with [FromKeyedServices], [ServiceKey] and [IocInject].
String Keys
public interface ICache;
[IocRegister<ICache>(Key = "memory")]
internal class MemoryCache : ICache;
[IocRegister<ICache>(Key = "distributed")]
internal class DistributedCache : ICache;Generated Code
// <auto-generated/>
services.AddKeyedSingleton<global::MyNamespace.MemoryCache, global::MyNamespace.MemoryCache>("memory");
services.AddKeyedSingleton<global::MyNamespace.ICache>("memory", (global::System.IServiceProvider sp, object? key) => sp.GetRequiredKeyedService<global::MyNamespace.MemoryCache>(key));
services.AddKeyedSingleton<global::MyNamespace.DistributedCache, global::MyNamespace.DistributedCache>("distributed");
services.AddKeyedSingleton<global::MyNamespace.ICache>("distributed", (global::System.IServiceProvider sp, object? key) => sp.GetRequiredKeyedService<global::MyNamespace.DistributedCache>(key));Enum Keys
public enum CacheType { Memory, Distributed }
[IocRegister<ICache>(Key = CacheType.Memory)]
internal class MemoryCache : ICache;
[IocRegister<ICache>(Key = CacheType.Distributed)]
internal class DistributedCache : ICache;Generated Code
// <auto-generated/>
services.AddKeyedSingleton<global::MyNamespace.MemoryCache, global::MyNamespace.MemoryCache>(global::MyNamespace.CacheType.Memory);
services.AddKeyedSingleton<global::MyNamespace.ICache>(global::MyNamespace.CacheType.Memory, (global::System.IServiceProvider sp, object? key) => sp.GetRequiredKeyedService<global::MyNamespace.MemoryCache>(key));
services.AddKeyedSingleton<global::MyNamespace.DistributedCache, global::MyNamespace.DistributedCache>(global::MyNamespace.CacheType.Distributed);
services.AddKeyedSingleton<global::MyNamespace.ICache>(global::MyNamespace.CacheType.Distributed, (global::System.IServiceProvider sp, object? key) => sp.GetRequiredKeyedService<global::MyNamespace.DistributedCache>(key));C# Expression Keys
Use KeyType.Csharp for compile-time expressions:
public static class CacheKeys
{
public static readonly Guid Primary = Guid.CreateVersion7();
}
[IocRegister<ICache>(Key = nameof(CacheKeys.Primary), KeyType = KeyType.Csharp)]
internal class PrimaryCache : ICache;Generated Code
// <auto-generated/>
services.AddKeyedSingleton<global::MyNamespace.PrimaryCache, global::MyNamespace.PrimaryCache>(global::MyNamespace.CacheKeys.Primary);
services.AddKeyedSingleton<global::MyNamespace.ICache>(global::MyNamespace.CacheKeys.Primary, (global::System.IServiceProvider sp, object? key) => sp.GetRequiredKeyedService<global::MyNamespace.PrimaryCache>(key));Injecting Keyed Services
Constructor Injection with [IocInject]
Use [IocInject] to inject keyed services. This will generate factory method registration:
[IocRegister<IMyService>]
internal class MyService([IocInject("memory")] ICache cache) : IMyService
{
private readonly ICache cache = cache;
}Generated Code
// <auto-generated/>
services.AddSingleton<global::MyNamespace.MyService>((global::System.IServiceProvider sp) =>
{
var p0 = sp.GetRequiredKeyedService<global::MyNamespace.ICache>("memory");
var s0 = new global::MyNamespace.MyService(p0);
return s0;
});
services.AddSingleton<global::MyNamespace.IMyService>((global::System.IServiceProvider sp) => sp.GetRequiredService<global::MyNamespace.MyService>());TIP
If you don't need KeyType.Csharp, use [FromKeyedServices] to align with MS.E.DI. See Using [FromKeyedServices].
Property Injection
[IocRegister<IMyService>]
internal class MyService : IMyService
{
[IocInject("distributed")]
public ICache Cache { get; init; } = null!;
}Generated Code
// <auto-generated/>
services.AddSingleton<global::MyNamespace.MyService>((global::System.IServiceProvider sp) =>
{
var s0_p0 = sp.GetRequiredKeyedService<global::MyNamespace.ICache>("distributed");
var s0 = new global::MyNamespace.MyService() { Cache = s0_p0 };
return s0;
});
services.AddSingleton<global::MyNamespace.IMyService>((global::System.IServiceProvider sp) => sp.GetRequiredService<global::MyNamespace.MyService>());Method Injection
[IocRegister<IMyService>]
internal class MyService : IMyService
{
private ICache cache = null!;
[IocInject]
public void Initialize([IocInject("memory")] ICache cache)
{
this.cache = cache;
}
}Generated Code
// <auto-generated/>
services.AddSingleton<global::MyNamespace.MyService>((global::System.IServiceProvider sp) =>
{
var s0_m0 = sp.GetRequiredKeyedService<global::MyNamespace.ICache>("memory");
var s0 = new global::MyNamespace.MyService();
s0.Initialize(s0_m0);
return s0;
});
services.AddSingleton<global::MyNamespace.IMyService>((global::System.IServiceProvider sp) => sp.GetRequiredService<global::MyNamespace.MyService>());Using [FromKeyedServices]
You can also use the standard [FromKeyedServices] attribute from Microsoft.Extensions.DependencyInjection.
NOTE
[FromKeyedServices] is natively handled by MS.E.DI, so the generator uses simple type-based registration instead of factory methods.
[IocRegister<IMyService>]
internal class MyService([FromKeyedServices("memory")] ICache cache) : IMyService
{
private readonly ICache cache = cache;
}Generated Code
// <auto-generated/>
services.AddSingleton<global::MyNamespace.MyService, global::MyNamespace.MyService>();
services.AddSingleton<global::MyNamespace.IMyService>((global::System.IServiceProvider sp) => sp.GetRequiredService<global::MyNamespace.MyService>());Difference from [IocInject]
[IocInject] attribute requires factory method registration because it's a SourceGen.Ioc-specific feature that MS.E.DI doesn't recognize:
[IocRegister<IMyService>]
internal class MyService([IocInject("memory")] ICache cache) : IMyService
{
private readonly ICache cache = cache;
}Generated Code
// <auto-generated/>
services.AddSingleton<global::MyNamespace.MyService>((global::System.IServiceProvider sp) =>
{
var p0 = sp.GetRequiredKeyedService<global::MyNamespace.ICache>("memory");
var s0 = new global::MyNamespace.MyService(p0);
return s0;
});
services.AddSingleton<global::MyNamespace.IMyService>((global::System.IServiceProvider sp) => sp.GetRequiredService<global::MyNamespace.MyService>());Using [Inject] from Microsoft.AspNetCore.Components
You can also use any attribute that name is [Inject], for example Microsoft.AspNetCore.Components.InjectAttribute:
[IocRegister<IMyService>]
internal class MyService : IMyService
{
[Inject(Key = "memory")]
public ICache Cache { get; init; } = null!;
}Generated Code
// <auto-generated/>
services.AddSingleton<global::MyNamespace.MyService>((global::System.IServiceProvider sp) =>
{
var s0_p0 = sp.GetRequiredKeyedService<global::MyNamespace.ICache>("memory");
var s0 = new global::MyNamespace.MyService() { Cache = s0_p0 };
return s0;
});
services.AddSingleton<global::MyNamespace.IMyService>((global::System.IServiceProvider sp) => sp.GetRequiredService<global::MyNamespace.MyService>());Using [ServiceKey]
You can use [ServiceKey] attribute to inject the key from service registration:
[IocRegister(Key = "Key")]
public class KeyService
{
[IocInject]
public void Initialize([ServiceKey] string key)
{
Console.WriteLine($"Service Key: {key}");
}
}Generated Code
// <auto-generated/>
services.AddKeyedTransient<global::MyNamespace.KeyService>("Key", (global::System.IServiceProvider sp, object? key) =>
{
var s0_m0 = "Key";
var s0 = new global::MyNamespace.KeyService();
s0.Initialize(s0_m0);
return s0;
});Diagnostics
| ID | Severity | Description |
|---|---|---|
| SGIOC006 | Warning | Both [FromKeyedServices] and [IocInject] are applied to the same parameter. [FromKeyedServices] takes precedence. |
| SGIOC013 | Error | [ServiceKey] parameter type does not match the registered key type from [IocRegister] or [IocRegisterFor]. |
| SGIOC014 | Warning | [ServiceKey] is applied to a parameter, but no Key is specified in [IocRegister] or [IocRegisterFor]. |
| SGIOC015 | Warning | Injected KeyValuePair<K, V>/dictionary key type K is incompatible with the registered keyed services for V. |