Keyed Services¶
Keyed services allow multiple implementations of the same interface to be registered with different keys, enabling consumers to resolve specific implementations.
Registering Keyed Services¶
Use the [Keyed] attribute to register a service with a specific key:
public interface ICacheProvider
{
string Name { get; }
}
[Keyed("redis")]
public sealed class RedisCacheProvider : ICacheProvider
{
public string Name => "redis";
}
[Keyed("memory")]
public sealed class MemoryCacheProvider : ICacheProvider
{
public string Name => "memory";
}
This generates registrations equivalent to:
services.AddKeyedSingleton<ICacheProvider, RedisCacheProvider>("redis");
services.AddKeyedSingleton<ICacheProvider, MemoryCacheProvider>("memory");
Consuming Keyed Services¶
Use the [FromKeyedServices] attribute (from Microsoft.Extensions.DependencyInjection) on constructor parameters to resolve keyed services:
public sealed class CacheManager
{
private readonly ICacheProvider _primaryCache;
private readonly ICacheProvider _fallbackCache;
public CacheManager(
[FromKeyedServices("redis")] ICacheProvider primaryCache,
[FromKeyedServices("memory")] ICacheProvider fallbackCache)
{
_primaryCache = primaryCache;
_fallbackCache = fallbackCache;
}
}
Mixing Keyed and Regular Dependencies¶
You can combine keyed and regular (unkeyed) dependencies in the same constructor:
public sealed class OrderService
{
public OrderService(
[FromKeyedServices("primary")] IPaymentProcessor processor,
ILogger<OrderService> logger) // Regular dependency
{
// ...
}
}
Registering via Plugin¶
For more complex registration scenarios, use IServiceCollectionPlugin:
public sealed class PaymentServicesPlugin : IServiceCollectionPlugin
{
public void Configure(ServiceCollectionPluginOptions options)
{
options.Services.AddKeyedSingleton<IPaymentProcessor, StripeProcessor>("stripe");
options.Services.AddKeyedSingleton<IPaymentProcessor, PayPalProcessor>("paypal");
}
}
Notes¶
[Keyed]can be applied multiple times to register the same class with different keys- Keyed services respect the same lifetime rules as regular services
- The key is a string value passed to
[FromKeyedServices("key")]when resolving - This feature requires .NET 8+ (where keyed services were introduced)
- Both source-gen and reflection paths support keyed services