Search code examples
c#asp.net-core.net-coredependency-injection

How to register multiple implementations of the same interface with different dependencies


Let's say I have multiple implementations of an interface. Every implementation has its own dependencies. How can we get the correct one? The correct implementation has to be chosen at runtime. During runtime some properties has to be set.

We have now the following working with AutoFac but in the new project we don't use it anymore. Now we can't get UploaderFactory to work.

If anyone has a better solution let me know!

// Interface with multiple implementations
public interface IFileUploader
{
    Task UploadAsync(string filePath);
}

// Dependency interface for a particular implementation
public interface IDependencyA
{
}

// Dependency interface for another implementation
public interface IAnotherDependency
{
}


public class FileUploaderA: IFileUploader
{
    public delegate FileUploaderA Factory(SomeStuff properties);

    private readonly SomeStuff _properties;
    private readonly IDependencyA _dependencyA;

    public FileUploaderA(SomeStuff properties, IDependencyA dependencyA)
    {
        _properties = properties;
        _dependencyA = dependencyA;
    }

    public Task UploadAsync(string filePath)
    {
        // Upload file and use properties and dependencies
        throw new NotImplementedException();
    }
}

public class FileUploaderB : IFileUploader
{
    public delegate FileUploaderB Factory(SomeStuff properties);

    private readonly SomeStuff _properties;
    private readonly IAnotherDependency _anotherDependency;

    public FileUploaderB(SomeStuff properties, IAnotherDependency anotherDependency)
    {
        _properties = properties;
        _anotherDependency = anotherDependency;
    }

    public Task UploadAsync(string filePath)
    {
        // Upload file and use properties and dependencies
        throw new NotImplementedException();
    }
}


public interface IUploaderFactory
{
    IFileUploader Create(bool condition, SomeStuff someStuff);
}

public class UploaderFactory : IUploaderFactory
{
    private readonly FileUploaderA.Factory _fileUploaderA;
    private readonly FileUploaderB.Factory _fileUploaderB;

    // This injection doesn't work!!!
    public UploaderFactory(FileUploaderA.Factory fileUploaderA, FileUploaderB.Factory fileUploaderB)
    {
        _fileUploaderA = fileUploaderA;
        _fileUploaderB = fileUploaderB;
    }

    public IFileUploader Create(bool condition, SomeStuff someStuff)
    {
        if (condition == true)
        {
            // Get File uploader A
            return _fileUploaderA(someStuff);
        }
        else
        {
            // Get File uploader B
            return _fileUploaderB(someStuff);
        }
    }
}

public class SomeStuff()
{
    public string A { get; set; }

    public string B { get; set; }
}

Solution

  • If I've read your requirements correctly, this should work. If not tell me what I got wrong. There are several ways to achieve this.

    The uploaders are no longer services: they are created by the factory. It uses the ActivatorUtilities class to create an instance of an object and populate the services from the service provider.

    First your base services:

    public interface IDependencyA { }
    
    public interface IAnotherDependency { }
    
    public class DependencyA : IDependencyA { }
    
    public class AnotherDependency : IAnotherDependency { }
    

    Your uploaders:

    public interface IFileUploader
    {
        Task UploadAsync(string filePath);
    }
    
    public class FileUploaderA : IFileUploader
    {
        private readonly SomeStuff _properties;
        private readonly IDependencyA _dependencyA;
    
        public FileUploaderA(SomeStuff properties, IDependencyA dependencyA)
        {
            _properties = properties;
            _dependencyA = dependencyA;
        }
    
        public Task UploadAsync(string filePath)
        {
            // Upload file and use properties and dependencies
            throw new NotImplementedException();
        }
    }
    
    public class FileUploaderB : IFileUploader
    {
        private readonly SomeStuff _properties;
        private readonly IAnotherDependency _anotherDependency;
    
        public FileUploaderB(SomeStuff properties, IAnotherDependency anotherDependency)
        {
            _properties = properties;
            _anotherDependency = anotherDependency;
        }
    
        public Task UploadAsync(string filePath)
        {
            // Upload file and use properties and dependencies
            throw new NotImplementedException();
        }
    }
    

    And the new factory:

    
    public interface IUploaderFactory
    {
        IFileUploader Create(bool condition, SomeStuff someStuff);
    }
    
    public class UploaderFactory : IUploaderFactory
    {
        private readonly IServiceProvider _serviceProvider;
    
        // This injection doesn't work!!!
        public UploaderFactory(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
    
        public IFileUploader Create(bool condition, SomeStuff someStuff)
        {
            IFileUploader uploader;
    
            // creates the equivalent of a transient Service
            if (condition == true)
            {
                uploader = ActivatorUtilities.CreateInstance<FileUploaderA>(_serviceProvider, someStuff);
            }
            else
            {
                uploader = ActivatorUtilities.CreateInstance<FileUploaderB>(_serviceProvider, someStuff);
            }
    
            return uploader;
        }
    }
    

    And finally service definition:

    public static class MyServiceExtensions
    {
        public static void AddMyServices(this IServiceCollection services)
        {
            services.AddScoped<IDependencyA, DependencyA>();
            services.AddScoped<IAnotherDependency, AnotherDependency>();
            services.AddScoped<IUploaderFactory, UploaderFactory>();
        }
    }