In this lesson, you will learn how to leverage keyed services and custom factory patterns to manage complex, context-specific dependencies within your Blazor components.
Keyed Services for Contextual Injection
Often, a component needs different implementations of the same interface depending on its configuration or role. While standard DI maps one interface to one implementation, Keyed Services allow you to register multiple implementations of the same interface under unique identifiers (keys).
Imagine a INotificationService with different behaviors for "Email" and "SMS" channels. You register them like this:
builder.Services.AddKeyedScoped<INotificationService, EmailService>("Email");
builder.Services.AddKeyedScoped<INotificationService, SMSService>("SMS");
In your Razor component, you then use the [Inject] attribute with the [KeyedService] metadata to resolve the exact dependency required:
@inject [FromKeyedServices("Email")] INotificationService EmailNotifier
Complex Factory Patterns
When a dependency requires runtime configuration—such as a user-specific API token or a dynamically computed state—static injection is insufficient. Here, we use a Factory Pattern injected via DI. Instead of injecting the service directly, you inject a factory that constructs the service using runtime parameters.
Consider a component that fetches data from various regional endpoints:
public class DataClientFactory
{
private readonly IServiceProvider _serviceProvider;
public DataClientFactory(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;
public IDataClient Create(string region) => region switch
{
"EU" => _serviceProvider.GetRequiredKeyedService<IDataClient>("EU"),
"US" => _serviceProvider.GetRequiredKeyedService<IDataClient>("US"),
_ => throw new ArgumentException("Invalid region")
};
}
By registering this factory as a singleton, you decouple your component from the logic of choosing and instantiating the service. Your component simply asks the factory for the object it needs:
@inject DataClientFactory ClientFactory
...
var client = ClientFactory.Create(userRegion);
This pattern ensures your components remain clean and focused on UI logic, while the complexities of object lifecycle and conditional instantiation remain encapsulated within the infrastructure layer.
Key Takeaway: Use keyed services to handle multiple implementations of the same interface, and employ factory patterns to inject dependencies that require runtime data for initialization.