DI - Múltiplas implementações da mesma interface
Hoje veremos como trabalhar com múltiplas implementações da mesma interface. |
Hoje veremos como registrar várias implementações de uma interface com o contêiner IoC da plataforma .NET em uma aplicação ASP.NET Core e recuperar um serviço específico em tempo de execução.
O contêiner de injeção de dependência nativo da plataforma .NET realiza um bom trabalho e atende a praticamente a maioria das necessidades relacionadas com o serviço de injeção de dependência. No entanto, lidar com várias implementações de uma interface ao trabalhar com injeção de dependência no ASP.NET Core é um pouco complicado.
O contêiner IoC
nativo da plataforma .NET não permite realizar o registro de vários serviços e,
em seguida, recuperar uma instância de um serviço específico em tempo de
execução.
Existem alguns contêineres IoC que permitem registrar tipos concretos usando uma
chave exclusiva que distingue as instâncias desses tipos. No entanto, o
contêiner IoC interno da plataforma .NET e usado pela ASP.NET Core não tem
suporte a este recurso. Portanto, registrar serviços que possuem uma interface
comum e resolvê-los em tempo de execução não é simples.
Para entender o problema vamos criar um projeto ASP .NET Core Web API no .NET 6.
Abra o VS 2022, clique em New Project e selecione o template ASP .NET Core Web API e clique em Next;
Informe o nome Api_DI e clique em Next;
A seguir selecione o Framework, Authentication Type e demais configurações conforme mostra a figura:
Crie uma pasta Services no projeto e a seguir crie a interface ICustomLogger:
namespace Api_DI.Services; public interface ICustomLogger { public string Write(string data); } |
A seguir vamos criar 3 implementações desta interface criando as classes concretas:
1- FileLogger
namespace Api_DI.Services; |
2- DBLogger
namespace Api_DI.Services; |
3- EventLogger
namespace Api_DI.Services; |
A classe FileLogger é usada para registrar dados em um arquivo, a classe DbLogger é usada para registrar dados em um banco de dados e a classe EventLogger é usada para registrar dados no log de eventos.
Agora vamos registrar esses serviços no contêiner DI nativo da plataforma .NET. Para isso vamos abrir o arquivo Program e definir o seguinte código:
using Api_DI.Services; var builder = WebApplication.CreateBuilder(args);
// Add services to the container. builder.Services.AddScoped<ICustomLogger, FileLogger>(); var app = builder.Build(); // Configure the HTTP request pipeline. app.UseHttpsRedirection();
|
A seguir vamos criar um controlador HomeController na pasta Controllers e definir o código abaixo para usar as instâncias dos serviços injetados no construtor do controlador:
using Api_DI.Services; using Microsoft.AspNetCore.Mvc; namespace Api_DI.Controllers;
[Route("api/[controller]")] [HttpGet] |
Se colocarmos um breakpoint no construtor e executarmos o projeto veremos que a instância obtida para obj1 , obj2 e obj3 será a instância de eventLogger :
Isso ocorre porque ele foi injetado por último e assim prevalece sobre os
demais. Assim percebemos que usando a abordagem padrão não há como obter uma
instância específica.
A seguir veremos uma implementação para contornar esta limitação do contêiner DI nativa da plataforma .NET.
Resolvendo o problema
A abordagem usada para resolver o problema vai usar uma enumeração e um delegate Func<> para registrar os serviços usando uma chave para identificar cada serviço com base na enumeração definida.
Vamos criar na pasta Services uma enumeração chamada ServiceEnum para identificar cada serviço :
namespace Api_DI.Services; public enum ServiceEnum { File, Database, Event } |
A seguir vamos registrar os serviços na classe Program e definir um serviço usando um delegate Func<ServiceEnum,ICustomLogger> para poder retornar instâncias das classes FileLogger, DbLogger e EventLogger dependendo do tipo de serviço selecionado em tempo de execução.
builder.Services.AddScoped<FileLogger>(); builder.Services.AddScoped<DBLogger>(); builder.Services.AddScoped<EventLogger>();
builder.Services.AddTransient<Func<ServiceEnum, ICustomLogger>>(serviceProvider => key => |
Agora vamos criar um controlador HomeController e usar as instâncias específicas de cada serviço:
using Api_DI.Services; using Microsoft.AspNetCore.Mvc; namespace Api_DI.Controllers;
[Route("api/[controller]")] [HttpGet("file")] [HttpGet("db/{id:int}")] [HttpGet("event/{nome}")] |
Executando o projeto teremos o seguinte resultado:
Executando cada endpoint iremos obter a instância específica do serviço e executar a operação desejada.
Pegue o projeto completo aqui : Api_DI.zip
E estamos conversados...
"Todo aquele, pois, que escuta estas
minhas palavras, e as pratica, assemelhá-lo-ei ao homem prudente, que
edificou a sua casa sobre a rocha;"
Mateus 7:24
Referências:
ASP .NET Core - Implementando a segurança com
ASP.NET Core MVC - Criando um Dashboard .
C# - Gerando QRCode - Macoratti
ASP .NET - Gerando QRCode com a API do Google
ASP .NET Core 2.1 - Como customizar o Identity
Usando o ASP .NET Core Identity - Macoratti
ASP .NET Core - Apresentando o IdentityServer4
ASP .NET Core 3.1 - Usando Identity de cabo a rabo