How Can Implement Multiple Interfaces in .Net 8 ?
When registering services with the built-in container, you can only control three things:
- The
Lifetime
—this controls how often an instance of your service should be reused, and can be one of three values:Transient
,Scoped
, orSingleton
. - The
ServiceType
—this is the type that you "request" in your constructors. It may be an interface, likeIWidget
, or a concrete type, likeWidget
. - The
ImplementationType
/instance—this is the type (or instance) that is used to satisfy theServiceType
dependency, for exampleWidget
.
We can have 2 approach to implement multiple interface, first one is Create a Dictionary and in key set a name and in value we can set Interface instance.
we have a Payment interface :
IPaymentService
{
public Task<TokenResult> RequestToken(RequestModel requestToken, CancellationToken cancellationToken = default);
}
private readonly Dictionary<string, IPaymentService>
_paymentServices = new Dictionary<string, IPaymentService>();
and we can use it with this one line of code:
var paymentService = _paymentServices[gate];
In the below we have 3 types of payment:
public class MelliService(IEpaymentApi epaymentApi):IPaymentService
{
public async Task<TokenResult> RequestToken(RequestModel requestToken, CancellationToken cancellationToken = default)
{
return await epaymentApi.RequestToken("melli", requestToken.PersonageId, requestToken.Amount,
requestToken.BankAccountUsage, requestToken.DeviceType, requestToken.CallbackUrl, cancellationToken);
}
}
public class MellatService(IEpaymentApi epaymentApi):IPaymentService
{
public async Task<TokenResult> RequestToken(RequestModel requestToken, CancellationToken cancellationToken = default)
{
return await epaymentApi.RequestToken("mellat", requestToken.PersonageId, requestToken.Amount,
requestToken.BankAccountUsage, requestToken.DeviceType, requestToken.CallbackUrl, cancellationToken);
}
}
public class SamanService(IEpaymentApi epaymentApi):IPaymentService
{
public async Task<TokenResult> RequestToken(RequestModel requestToken, CancellationToken cancellationToken = default)
{
return await epaymentApi.RequestToken("sep", requestToken.PersonageId, requestToken.Amount,
requestToken.BankAccountUsage, requestToken.DeviceType, requestToken.CallbackUrl, cancellationToken);
}
}
and in Dotnet core for Inject this Interfaces we must add these in service.
services.AddScoped<IEPaymentService, SamanService>();
services.AddScoped<IEPaymentService, MellatService>();
services.AddScoped<IEPaymentService, MelliService>();
Now we have a list of Dependencies of IPaymentService and we can get in constructor of classes.
public class MelliService(IEnumerable<IPaymentService> services){}
But in Second Approach that introduced in Dotnet 8 is Keyed service dependency. In this method you can Use AddKeyedScoped instead of AddScoped and give a argument for that method.
var builder = WebApplication.CreateBuilder(args);
services.AddKeyedScoped<IEPaymentService, SamanService>(BackofficeOptions.SamanBankService);
services.AddKeyedScoped<IEPaymentService, MellatService>(BackofficeOptions.MellatBankService);
services.AddKeyedScoped<IEPaymentService, MelliService>(BackofficeOptions.MelliBankService);
And Inject these Interface and use that implementation with the below code.
[FromKeyedServices("Melli")] IEPaymentService sms
What are keyed services?
Dependency injection (DI) is everywhere in ASP.NET Core. You use it with your custom services, but perhaps more importantly, the framework itself uses DI throughout. Most everything that you can configure in ASP.NET Core is configured via DI.
With keyed services, another piece of information is stored with the ServiceDescriptor
, a ServiceKey
that identifies the service. The key can be any object, but it will commonly be a string
or an enum
(something that can be a constant so it can be used in attributes). For non-keyed services, the ServiceType
identifies the registration; for keyed services, the combination of ServiceType
and ServiceKey
identifies the registration.
Using keyed services to retrieve specific service instances
Before I show an example, I’ll point out a current glaring limitation: neither minimal APIs nor MVC currently support keyed services directly. There are issues logged for these here and here, but the lack of support makes the following examples more convoluted than I’d have liked 😅
Keyed services are useful when you have an interface/service with multiple implementations that you want to use in your app. What’s more, you need to use each of those implementations in different places in your app.