How to Register Multiple Implementations of the Same Interface in ASP.NET Core

How to register multiple implementations of the same interface in Asp.Net Core?

I did a simple workaround using Func when I found myself in this situation.

Firstly declare a shared delegate:

public delegate IService ServiceResolver(string key);

Then in your Startup.cs, setup the multiple concrete registrations and a manual mapping of those types:

services.AddTransient<ServiceA>();
services.AddTransient<ServiceB>();
services.AddTransient<ServiceC>();

services.AddTransient<ServiceResolver>(serviceProvider => key =>
{
switch (key)
{
case "A":
return serviceProvider.GetService<ServiceA>();
case "B":
return serviceProvider.GetService<ServiceB>();
case "C":
return serviceProvider.GetService<ServiceC>();
default:
throw new KeyNotFoundException(); // or maybe return null, up to you
}
});

And use it from any class registered with DI:

public class Consumer
{
private readonly IService _aService;

public Consumer(ServiceResolver serviceAccessor)
{
_aService = serviceAccessor("A");
}

public void UseServiceA()
{
_aService.DoTheThing();
}
}

Keep in mind that in this example the key for resolution is a string, for the sake of simplicity and because OP was asking for this case in particular.

But you could use any custom resolution type as key, as you do not usually want a huge n-case switch rotting your code. Depends on how your app scales.

Register multiple implementations to same interface

Unfortunately I can't find "named instances" functionality in embeded ASP.NET Core DI container. In Autofac you can use Named and Keyed Services.

In your case you can do something like this:

public enum RepoTypes { Blob, Quarantine }

...

public interface IRepoResolver
{
//resolve using method
IBlobStorageRepository GetRepo(RepoTypes rt);

//resolve usnig indexer
IBlobStorageRepository this[RepoTypes rt] { get; }
}

...

public sealed RepoResolver : IRepoResolver
{
private readonly Dictionary<RepoTypes, IBlobStorageRepository> _repos;

public RepoResolver()
{
_repos[RepoTypes.Blob] = new BlobStorageRepository(...);
_repos[RepoTypes.Quarantine] = new BlobStorageRepository(...);
}

public IBlobStorageRepository GetRepo(RepoTypes rt) => _repos[rt];

public IBlobStorageRepository this[RepoTypes rt] { get => _repos[rt]; }
}

...

services.AddSingleton<IRepoResolver, RepoResolver>();

...

public class Some
{
public Some(IRepoResolver rr)
{
var blob1 = rr.GetRepo(RepoTypes.Blob);
var blob2 = rr[RepoTypes.Blob];
}
}

How to register multiple implementations with its own interface in ASP.NET Core using Reflection?

Something like this would work

// Get all classes implementing IAsyncRepository
var repositoryTypes = assembly.GetTypes().Where(x => !x.IsInterface &&
x.GetInterface(typeof(IAsyncRepository<>).Name) != null);
foreach (var repositoryType in repositoryTypes)
{
var type = repositoryType.UnderlyingSystemType;
services.AddSingleton(type.GetInterface($"I{type.Name}"), type);
}

I'm not sure if there is a better way to get the interface type

How to register multiple implementation for same interface using .NET CORE DI

There are actually two points of discussion in your question. One is a point related to the design of the code, the other one is a point related on how to use the .NET core DI container in order to handle the required registrations.
Both of them are important, but we need to treat them one at a time.

How to organize the code

To solve your problem in a clean and extensibile way you need to use a design pattern known as the composite design pattern. In order to do so, you need to change the definition of your interface to the following:

public interface IMessageConsumer 
{
bool CanHandleMessage(Message message);
Task HandleMessage(Message message);
}

Your interface implementations are then changed as follows:

public class FooMessageConsumer: IMessageConsumer 
{
public bool CanHandleMessage(Message message)
{
if (message is null) throw new ArgumentNullException(nameof(message));

return message.Type == "foo";
}

public Task HandleMessage(Message message)
{
if (message is null)
throw new ArgumentNullException(nameof(message));

if (!this.CanHandleMessage(message))
throw new InvalidOperationException($"{nameof(FooMessageConsumer)} can only handle foo messages.");

await Task.Delay(100).ConfigureAwait(false);

Console.Writeline($"Message {message.Id} handled by {nameof(FooMessageConsumer)}");
}
}

public class BarMessageConsumer: IMessageConsumer
{
public bool CanHandleMessage(Message message)
{
if (message is null) throw new ArgumentNullException(nameof(message));

return message.Type == "bar";
}

public Task HandleMessage(Message message)
{
if (message is null)
throw new ArgumentNullException(nameof(message));

if (!this.CanHandleMessage(message))
throw new InvalidOperationException($"{nameof(BarMessageConsumer)} can only handle bar messages.");

await Task.Delay(100).ConfigureAwait(false);

Console.Writeline($"Message {message.Id} handled by {nameof(BarMessageConsumer)}");
}
}

At this point you need to introduce a special message consumer, which will be used to dispatch the message to the proper consumer. This is called the composite message consumer and this is the implementation of IMessageConsumer that you will register in your DI container and that will be injected in all the classes which need a message consumer in order to do their business.

public class CompositeMessageConsumer : IMessageConsumer 
{
private readonly IMessageConsumer[] _consumers;

public CompositeMessageConsumer(IEnumerable<IMessageConsumer> consumers)
{
if (consumers is null)
throw new ArgumentNullException(nameof(consumers));

this._consumers = consumers.ToArray();
}

public bool CanHandleMessage(Message message)
{
if (message is null) throw new ArgumentNullException(nameof(message));

return this._consumers.Any(c => c.CanHandleMessage(message));
}

public async Task HandleMessage(Message message)
{
if (message is null)
throw new ArgumentNullException(nameof(message));

if (!this.CanHandleMessage(message))
throw new InvalidOperationException("None of the available consumers is able to handle the provided message.");

var consumer = this._consumers.First(c => c.CanHandleMessage(message));
await consumer.HandleMessage(message).ConfigureAwait(false);
}
}

Here is an example of a class which uses the IMessageConsumer interface. At runtime, the DI container will inject an instance of CompositeMessageConsumer.

// this is an example of a class depending on the IMessageConsumer service
public class MessageProcessor
{
// at runtime this will be an instance of CompositeMessageConsumer
private readonly IMessageConsumer _consumer;

// the DI container will inject an instance of CompositeMessageConsumer here
public MessageProcessor(IMessageConsumer consumer)
{
if (consumer is null) throw new ArgumentNullException(nameof(consumer));

this._consumer = consumer;
}

public async Task ProcessIncomingMessage(Message message)
{
if (message is null) throw new ArgumentNullException(nameof(message));

// do all the pre processing here...

// handle the message
await this._consumer.HandleMessage(message).ConfigureAwait(false);

// do all the post processing here...
}
}

How to register the services on the .NET core DI container

Deciding the proper lifetime for your registrations is a problem that goes beyond the scope of this discussion.

In my example code above I have defined stateless consumer classes and the composite consumer only iterates over the array of the available consumers. The array is never modified during the iteration. This means that all the involved classes are thread safe, so we can register all of them with a singleton lifetime.

That said, the simplest registration that you can perform is the following:

// register the consumers as classes
services.AddSingleton<FooMessageConsumer>();
service.AddSingleton<BarMessageConsumer>();

// register the composite message consumer as an interface, so that when you require IMessageConsumer you get CompositeMessageConsumer
services.AddSingleton<IMessageConsumer>(container =>
{
var fooConsumer = container.GetRequiredService<FooMessageConsumer>();
var barConsumer = container.GetRequiredService<BarMessageConsumer>();

return new CompositeMessageConsumer(new IMessageConsumer[]
{
fooConsumer,
barConsumer
});
});

A great book to learn about these topics is this one. If you are a .NET developer this is definitely a must read.

Dependency Injection in .NET Core 3.1 with multiple implementations

you need to add the following in configureservices of Startup.cs:

var factory = ActivatorUtilities.CreateFactory(typeof(ClassB), new Type[] {typeof(IClass)});
services.AddSingleton<ClassB>(sp => factory.Invoke(sp, new object[] {sp.GetService<ClassA>()}) as ClassB); //when calling sp.GetService<ClassA> you can of course pass any implementation of IClass instead of ClassA. Of course you cannot pass ClassB beacause of infinite recursion.

With this solution you are telling the IoC container that you want to register a service for ClassB wich:

  • will have his dependency of type "IClass" resolved as "ClassA" as it was declared in the IoC container
  • will have all other dependencies normally resolved by the IoC container

How to register two implementations then get one in .Net Core dependency injection

The container knows how to resolve a FirstImplementation when asked for the MyInterface, how ever is was not told how to resolve a FirstImplementation when asked specifically for a FirstImplementation.

The built-in services container is meant to serve the basic needs of the framework and most consumer applications built on it. It is bare bones and needs to be configured explicitly to behave as desired. You will need to also tell it how to get the implementations when explicitly asked for the implementations

//register implementations first
services.AddSingleton<FirstImplementation>();
services.AddSingleton<SecondImplementation>();

//register interfaces using factory that return implementation singleton
services.AddSingleton<MyInterface, FirstImplementation>(p => p.GetService<FirstImplementation>());
services.AddSingleton<MyInterface, SecondImplementation>(p => p.GetService<SecondImplementation>());

So now you can get your FirstImplementation directly and get the same instance

var firstImplementation = serviceProvider.GetService<FirstImplementation>();

How to register multiple implementations using factory pattern and Microsoft.Extensions.DependencyInjection

You've registered the service by its interface:

services.AddScoped<IDeployApplicationService, ProdDeployApplicationService>()

This means you can only resolve it by the interface serviceProvider.GetRequiredService<IDeployApplicationService>(), and that would give you the last service registered by that interface, NonProdDeployApplicationService.

Try registering it by itself (the implementation):

services.AddScoped<ProdDeployApplicationService>();

Now you can resolve the implementation directly:

serviceProvider.GetRequiredService<ProdDeployApplicationService>();

Keep in mind you can register a class multiple times: both by itself and by the interfaces/abstract classes it implements:

// this is ok
services.AddScoped<ProdDeployApplicationService>();
services.AddScoped<IDeployService, ProdDeployApplicationService>();
services.AddScoped<IDeployApplicationService, ProdDeployApplicationService>();

and resolve it using any key.



Related Topics



Leave a reply



Submit