'How to register services with dependency container services by reflection in c#

I have an interface IProcessor and multiple processors implementing this interface:

Processor1 : IProcessor
Processor2 : IProcessor

I want to inject these processors into a class like: IEnumerable<IProcessor> so that I can run them one by one.

I can register these one by one with the microsoft dependency injection container, but I want to do it by reflection so that a newly added processor is registered automatically.

var processorInterface = typeof(IProcessor);

var processors = Assembly.GetExecutingAssembly().GetTypes()
               .Where(t => processorInterface.IsAssignableFrom(t) && t.IsClass && 
               !t.IsAbstract && t.IsPublic);                  

foreach (var processor in processors)
{
    serviceCollection.AddScoped(processor);

    // Below line does not compile of course
    //serviceCollection.AddScoped<IProcessor, processor>());
}

But this doesn't work, I always get an empty list injected.



Solution 1:[1]

An overload extension already exists for AddScoped that takes the service and implementation type.

/// Adds a scoped service of the type specified in <paramref name="serviceType"/> with an
/// implementation of the type specified in <paramref name="implementationType"/> to the
/// specified <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
/// <param name="serviceType">The type of the service to register.</param>
/// <param name="implementationType">The implementation type of the service.</param>
/// <returns>A reference to this instance after the operation has completed.</returns>
/// <seealso cref="ServiceLifetime.Scoped"/>
public static IServiceCollection AddScoped(
    this IServiceCollection services,
    Type serviceType,
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType)
{
    ThrowHelper.ThrowIfNull(services);
    ThrowHelper.ThrowIfNull(serviceType);
    ThrowHelper.ThrowIfNull(implementationType);

    return Add(services, serviceType, implementationType, ServiceLifetime.Scoped);
}

Source

Which when applied to your example would look like

//...

foreach (var processor in processors) {
    serviceCollection.AddScoped(processor);

    serviceCollection.AddScoped(processorInterface, processor);
}

//...

Solution 2:[2]

I would like to suggest a different approach to this by using attributes.

Here I created an Attribute which I called InjectableAttribute:

[AttributeUsage(AttributeTargets.Class)]
public class InjectableAttribute: Attribute
{
    public Type ImplementedInterface { get; }
    public DependencyInjectionScope Scope { get; }

    public InjectableAttribute(Type implementedInterface, DependencyInjectionScope scope = DependencyInjectionScope.Singleton)
    {
        ImplementedInterface = implementedInterface;
        Scope = scope;
    }
}

I created this enumeration to specify the scope of the dependency too:

public enum DependencyInjectionScope
{
    Singleton,
    PerDependency,
    Scoped
}

And last, I created this extention method to scan my assembly and inject all the classes that have InjectableAttribute:

namespace Microsoft.Extensions.DependencyInjection
{
    public static class DependencyInjectionExtensions
    {
        public static void RegisterDependencies(this IServiceCollection services, Assembly assembly)
        {
            var types = assembly
                .ExportedTypes
                .Where(x => x.GetCustomAttributes(typeof(InjectableAttribute), true).Length > 0)
                .ToList();

            for (var i = 0; i < types.Count; i++)
            {
                var currentType = types[i];
                var attributes = (InjectableAttribute[])currentType.GetCustomAttributes(typeof(InjectableAttribute),
                    true);
                var attribute = attributes[0];
                var implementedInterface = attribute.ImplementedInterface;
                switch (attribute.Scope)
                {
                    case DependencyInjectionScope.Scoped:
                        services.AddScoped(implementedInterface, currentType);
                        break;
                    case DependencyInjectionScope.PerDependency:
                        services.AddTransient(implementedInterface, currentType);
                        break;
                    case DependencyInjectionScope.Singleton:
                        services.AddSingleton(implementedInterface, currentType);
                        break;
                }
            }
        }
    }
    
}

Now to use this, here is an example of a service class:

[Injectable(typeof(ICustomerInfoService), DependencyInjectionScope.Scoped)]
public class CustomerInfoService : ICustomerInfoService
{

}

And to register eveything in my ConfigureServices method:

services.RegisterDependencies(typeof(Startup).Assembly);

Hope this helps :D

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Nkosi