Understanding Keyed Services in .NET 8

Introduction

Dependency injection plays a crucial role in every programming language.

Microsoft has introduced many new features and significant enhancements in .Net 8. “Keyed Services” is one of the important, powerful enhancements in the dependency injection shipped with .Net Core 8.

In this article, we will explore Keyed Services .Net 8, illustrating with an example. This article is suitable for beginners, intermediate learners, and professionals.

We are going to cover the following in this article.

  1. How do you deal with multiple implementations of the interface prior to Keyed Services?
  2. Introduction of Keyed Services?
  3. Lifetime scope for Keyed Services.
    • AddKeyedSingleton
    • AddKeyedScoped
    • AddKeyedTransient
  4. How to register and resolve dependencies using Keyed Services

Prerequisites

Below are the prerequisites for Keyed Services.

  1. .NET 8 SDK (RC1 or later version)
  2. Visual Studio 2022 version 17.8 or later

Let’s start with the use case of the Keyed Services.

How do you deal with multiple implementations of the interface prior to Keyed Services?

Let’s assume that we want to implement report generator functionality, which can generate reports on XML, CSV, and PDF as per the user's preference.

Now, think how you will implement this prior to .NET 8. Let’s create a demo MVC core project in .NET Core 6.0.

Step 1. Create a .NET Core MVC project using .NET Core 6.0.

Step 2. Create an Interface called “IReportGenerator”.

namespace WebApplication1.Interfaces
{
    public interface IReportGenerator
    {
        string GenerateReport();
    }
}

Step 3. Create Three classes “CSVReportGenerator”, “XmlReportGenerator”, and “And PDFReportGenerator” and implement the “IReportGenerator” interface.

CSVReportGenerator.cs

public class CSVReportGenerator : IReportGenerator
{
    public string GenerateReport()
    {
        // Actual code of CSV report generator
        return "CSV report";
    }
}

XmlReportGenerator.cs

public class XmlReportGenerator: IReportGenerator
{
   public string GenerateReport()
   {
     //actual code of xml report generator 
      return "XML Report";
   }
}

PDFReportGenerator.cs

public class PDFReportGenerator: IReportGenerator
{
   public string GenerateReport()
   {
      //actual code of xml report gen
      return "PDF Report";
   }
}

Step 4. Register dependencies in the program.cs file.

builder.Services.AddSingleton<IReportGenerator, XmlReportGenerator>(); 
builder.Services.AddSingleton<IReportGenerator, PDFReportGenerator>();
builder.Services.AddSingleton<IReportGenerator, CSVReportGenerator>(); 

We have registered all three classes in the program.cs file.

Step 5. Make the code changes in the HomeController.

public class HomeController: Controller
{
   private readonly ILogger<HomeController> _logger;
   private readonly IReportGenerator _reportGenerator;  
   public HomeController (ILogger<HomeController> logger, IReportGenerator reportGenerator)
   {
      _logger logger;
      _reportGenerator = reportGenerator;
   }
   public IActionResult Index()
   {
       ViewBag.ReportGenerator=_reportGenerator.GenerateReport();
       return View();
   }
}

In the above code, we have injected “IReportGenerator” as a parameter in the controller constructor, which is then called the GenerateReport method in the index action method.

Step 6. Make code changes in the Index.cshtml file.

In the code below, we are printing the GeneratorReport output in the welcome message.

@{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Welcome from @ViewBag.ReportGenerator</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

Step 7. Run the application and observe the output.

Output

Application

We observed that only the last registered service was retrieved. In this case, the CSV report is the last registered service.

Now, the next question is how to get another registered service, if required, like XML Report or PDF Report. We can make the code changes below in the HomeController and achieve it.

public class HomeController: Controller
{
  private readonly ILogger<HomeController> _logger;
  private readonly IEnumerable<IReportGenerator> _reportGenerator;
  public HomeController (ILogger<HomeController> logger, IEnumerable<IReportGenerator> reportGenerator)
  {
    _logger logger;
    _reportGenerator = reportGenerator;
  }
  public IActionResult Index()
  {
     ViewBag.ReportGenerator=_reportGenerator.ElementAt(0).GenerateReport();
     return View();
  }
}

Execute the program and see the output. This time, we will get the XmlReport.

Output

Introduction of Keyed Services

Keyed Services allows us to register and retrieve dependency injection using Key.

You can register and retrieve multiple implementations of the same interface using a key.

Lifetime scope for Keyed Services

Microsoft introduced three new extension methods for the Lifetime scope of keyed services.

  1. AddKeyedSingleton
  2. AddKeyedScoped
  3. AddKeyedTransient

These extension methods are like AddSinglton, AddScoped, and AddTransient methods in .Net core, except it allow us to use “key” to define lifetime scope of dependencies.

How to register and resolve dependencies using Keyed Services?

Let’s create the same report example using .Net 8 Keyed Service.

Step 1. Create a .Net Core MVC project using .Net Core 8.0

Step 2. Create interface “IReportGenerator” and three classes “CSVReportGenerator”, “PDFReportGenerator”, and “XMLReportGenerator” in the previous section of Step 2 and Step 3.

Step 3. Register dependencies in the Program.cs file like below.

builder.Services.AddKeyedSingleton<IReportGenerator, XmlReportGenerator>("XML"); 
builder.Services.AddKeyedSingleton<IReportGenerator, PDFReportGenerator>("PDF"); 
builder.Services.AddKeyedSingleton<IReportGenerator, CSVReportGenerator>("CSV");

we have used “AddKeyedSingleton” to register dependencies.

Step 4. Make the below code changes in HomeController.

public class HomeController: Controller
{
  private readonly ILogger<HomeController> _logger; 
  private readonly IReportGenerator _xmlreportGenerator; 
  private readonly IReportGenerator _pdfreportGenerator;

  public HomeController (ILogger<HomeController> logger, [FromKeyedServices ("XML")] IReport 
  Generator xmlReportGenerator, [FromKeyedServices ("PDF")] IReportGenerator pdfReportGenerator) 
  {
    _logger logger;
    _xmlreportGenerator = xmlReportGenerator;
    _pdfreportGenerator = pdfReportGenerator;
  }
  public IActionResult Index()
  {
    ViewBag.ReportGenerator = _xmlreportGenerator.GenerateReport() + "Second Report + 
    _pdfreportGenerator.GenerateReport();
    return View();
  }
}

In this code, we have used [FromKeyedServices] to retrieve dependencies based on the Key.

Step 5. Copy index.cshtml code from the previous example, step 6.

Step 6. Execute the program and see the output.

Output

Program

I hope you enjoyed this article and learned something new.


Similar Articles