.NET ASP.NET Core C#
Ranjithkumar  

Lookup DI services using keys in .NET 8

One of the new features in .NET 8 is the ability to dynamically lookup DI services using keys. This can be a useful way to decouple your code from the concrete implementation of a service.

For example, let’s say you have a service that sends notifications. You could have a concrete implementation for each type of notification, such as EmailNotificationService and SmsNotificationService.

Here is an example of how to register keyed DI services in ASP.NET Core:

public void ConfigureServices(IServiceCollection services)
{
    services.AddKeyedSingleton<INotificationService, EmailNotificationService>("email");
    services.AddKeyedSingleton<INotificationService, SmsNotificationService>("sms");
}

In the past, you would have to inject the specific service you needed into your constructor. For example, if you wanted to send an email notification, you would inject the EmailNotificationService into your constructor.

public class MyService
{
    private readonly EmailNotificationService _notificationService;

    public MyService(EmailNotificationService notificationService)
    {
        _notificationService = notificationService;
    }

    public void SendNotification()
    {
        _notificationService.SendEmail("...");
    }
}

With .NET 8, you can instead use the GetRequiredServiceByKey method to dynamically lookup the service you need. This allows you to decouple your code from the concrete implementation of the service.

public class MyService
{
    private readonly IServiceProvider _serviceProvider;

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

    public void SendNotification()
    {
        var notificationService = _serviceProvider.GetRequiredServiceByKey<INotificationService>("email");
        notificationService.SendNotification("...");
    }
}

In this example, the MyService class does not need to know the concrete implementation of the notification service. Instead, it uses the GetRequiredServiceByKey method to lookup the service it needs using a key.

The key is a string that uniquely identifies the service. In this example, the key is "email".

The GetRequiredServiceByKey method will throw an exception if the service cannot be found.

This feature can be a useful way to make your code more flexible and reusable. It can also help to reduce the coupling between your code and the concrete implementation of your services.

In addition to the GetRequiredServiceByKey method, there are also GetServiceByKey and TryGetServiceByKey methods available. The GetServiceByKey method will return null if the service cannot be found, and the TryGetServiceByKey method will return false if the service cannot be found.

Using Keyed DI in Factory pattern to avoid if statements

It looks like this if we don’t use keyed DI in factory pattern

public class NotificationServiceFactory
{
    private readonly IServiceProvider _serviceProvider;

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

    public INotificationService CreateNotificationService(string notificationType)
    {
        if (notificationType == "email")
        {
            return _serviceProvider.GetRequiredService<EmailNotificationService>();
        }
        else if (notificationType == "sms")
        {
            return _serviceProvider.GetRequiredService<SmsNotificationService>();
        }
        else
        {
            throw new ArgumentException($"Invalid notification type: {notificationType}");
        }
    }
}

Here is an example of how to use keyed DI with the factory pattern to avoid if statements:

public class NotificationServiceFactory
{
    private readonly IServiceProvider _serviceProvider;

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

    public INotificationService CreateNotificationService(string notificationType)
    {
        return _serviceProvider.GetRequiredServiceByKey<INotificationService>(notificationType);
    }
}

Conclusion

.NET 8 introduces a new feature that allows you to dynamically lookup DI services using keys. This can be a useful way to decouple your code from the concrete implementation of a service.

In this blog post, we showed how to use keyed DI with the factory pattern to avoid if statements. We also provided an example of how it looks without keys.

The use of keyed DI can help to make your code more flexible and reusable. It can also help to reduce the coupling between your code and the concrete implementation of your services.

Leave A Comment