Search code examples
c#asp.net-coredependency-injectionasp.net-core-7.0

Register types from assembly scan against interface and implementation in ASP.NET Core DI


To register some service against both interface and implementation:

services.AddSingleton<IFoo, Foo>();
services.AddSingleton<Foo>(x => x.GetRequiredService<IFoo>());

That ensures one can resolve both IFoo and Foo, and that the container would provide the same instance.

I'm trying to extend that concept to my use case:

I've written a library which performs an assembly scan at startup, and auto-registers types which implement IAnalyser. In client code I want to resolve both IEnumerable<IAnalyser> and FooAnalyser.

I'm unsure how to perform the registrations. This is what I have so far:

IEnumerable<Type> implementationTypes = ScanClientAssembliesForImplementationsOfIAnalyser();

foreach (var implementationType in implementationTypes)
{
  services.AddSingleton(typeof(IAnalyser), implementationType);  // (1) works
  //services.AddSingleton(implementationType);                   // (2) incorrect
  services.AddSingleton(x => /* ...?... */);                     // (3)
}

The registration in (1) correctly allows me to resolve IEnumerable<IAnalyser>.

The registration in (2) allows me to resolve FooAnalyser, but it would be a different instance, and is thus incorrect.

I think the correct approach is to register the same instance via a factory. How do I do that in (3)?

(Note my above approach may be wrong. All I want is to resolve both IEnumerable<IAnalyser> and FooAnalyser, in client code. Maybe there is a completely different way to do that?)


Solution

  • I found easier solution.

    There's one nice thing - when you register implementation for the interface, you always (out of the box) can resolve IEnumerable<interface> to get all registered implementations.

    Having said that, we could just loop over all types registering them as singleton for the type and register them as interface implementation using factory method, like below:

    // Registartion code
    IEnumerable<Type> implementationTypes = ScanClientAssembliesForImplementationsOfIAnalyser();
    
    foreach (var type in implementationTypes)
    {
        builder.Services.AddSingleton(type);
        builder.Services.AddSingleton(typeof(IAnalyser), sp => sp.GetRequiredService(type));
    }
    

    I have sample setup:

    public interface IAnalyser { }
    public class Analyzer1 : IAnalyser { }
    public class Analyzer2 : IAnalyser { }
    

    And then resolution (with minimal APIs, but that is of course working with controllers as well):

    app.MapGet("/weatherforecast", (IEnumerable<IAnalyser> analyzers, Analyzer1 a1, Analyzer2 a2) =>
    {
        var result = analyzers.ElementAt(0) == a1; // true
        result = analyzers.ElementAt(1) == a2; // true
    });