Articles in this section
Category / Section

Keyed Services

Published:
Keyed services in the context of software development, particularly with dependency injection (DI) frameworks, are services that are registered in the DI container with a specific key. This key is then used to resolve or retrieve the service instance from the DI container. The concept is particularly useful in scenarios where you need to select between multiple implementations of the same interface or base class at runtime, based on some criteria such as a tenant identifier in a multitenant application, a specific feature flag, or other runtime values.

The key can be of any type that supports equality comparison, but strings and enums are the most commonly used types for keys. This approach allows for more flexible and dynamic service resolution compared to the standard practice of resolving services solely by their type.

Here's a simplified conceptual overview of how keyed services might be used:
  1. Registration: During the application startup, multiple implementations of a particular interface are registered in the DI container. Each implementation is associated with a unique key.
  2. Resolution: At runtime, when a service is needed, the application determines the appropriate key based on the current context (e.g., the current user's tenant ID). It then requests the service instance associated with that key from the DI container.
  3. Usage: The DI container returns the correct implementation of the service based on the provided key, allowing the application to use the service as needed.

Keyed services are particularly useful in scenarios where the application needs to dynamically select among multiple service implementations at runtime, providing a flexible and powerful mechanism for managing dependencies in complex applications.

How to register keyed services in .NET Core.

To register keyed services in .NET Core, you typically need a way to map keys to service types and resolve these services based on the key at runtime. .NET Core's built-in dependency injection (DI) container does not support keyed or named registrations directly, unlike some other DI containers like Autofac. However, you can achieve similar functionality by using a factory pattern or by integrating a third-party DI container that supports keyed registrations.

Here's how you can implement keyed services using a factory pattern with the built-in .NET Core DI container:

Step 1: Define the Service Interface


First, define the base interface for your services.
public interface IMyService
{
    void DoWork();
}

Step 2: Implement the Service Interface for Different Keys

Create different implementations of the IMyService interface.
public class MyServiceA : IMyService
{
    public void DoWork() => Console.WriteLine("Service A is working.");
}

public class MyServiceB : IMyService
{
    public void DoWork() => Console.WriteLine("Service B is working.");
}

Step 3: Create a Factory to Resolve Services Based on a Key

Implement a factory that resolves services based on a key.
public class MyServiceFactory
{
    private readonly IServiceProvider _serviceProvider;

    public MyServiceFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IMyService GetService(string key)
    {
        return key switch
        {
            "A" => _serviceProvider.GetRequiredService<MyServiceA>(),
            "B" => _serviceProvider.GetRequiredService<MyServiceB>(),
            _ => throw new KeyNotFoundException($"Service with key {key} not found."),
        };
    }
}

Step 4: Register the Services and Factory in the DI Container

In your Startup.cs or wherever you configure services, register the implementations and the factory.
public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<MyServiceA>();
    services.AddTransient<MyServiceB>();
    services.AddSingleton<MyServiceFactory>();
}

Step 5: Use the Factory to Resolve Services

Now, you can inject the MyServiceFactory into your classes and use it to resolve the appropriate service based on a key.
public class MyController : Controller
{
    private readonly MyServiceFactory _serviceFactory;

    public MyController(MyServiceFactory serviceFactory)
    {
        _serviceFactory = serviceFactory;
    }

    public void SomeAction(string key)
    {
        var service = _serviceFactory.GetService(key);
        service.DoWork();
    }
}
This approach allows you to manually manage keyed service resolution in .NET Core using the built-in DI container. If you require more advanced scenarios or prefer a more integrated solution, consider using a third-party DI container like Autofac, which supports keyed and named registrations natively.


Access denied
Access denied