0

In my app, there is one interface and two subclasses. Two subclasses have been registered as services. My question is how to control which one to get when i use them?

HomeController.cs

public class HomeController : Controller
{
    public interface ITestDI
    {

    }

    public class TestDIClass1: ITestDI
    {
        public TestDIClass1()
        {

        }
    }

    public class TestDIClass2 : ITestDI
    {
        public TestDIClass2()
        {

        }
    }

    ITestDI td;

    public HomeController(ITestDI _td)
    {
        this.td = _td; // how to control which ITestDI implementation will injected with constructor injection? With the configuration below, always get TestDIClass2.
    }

    public IActionResult Index()
    {
        return View();
    }
} 

Startup.cs

services.AddScoped<ITestDI, TestDIClass1>();
services.AddScoped<ITestDI, TestDIClass2>(); // it seems like TestDIClass2 has overwrited the TestDIClass1.
5
  • 1
    The idea here is that your code depends on an interface and not a concrete implementation. If your code only works with one service or the other, they should probably have different interfaces. Commented Sep 25, 2018 at 3:35
  • How do you call HomeController(ITestDI _td)? Commented Sep 25, 2018 at 3:36
  • For public HomeController(ITestDI _td), what is the logic to decide which instance to return? Commented Sep 25, 2018 at 5:14
  • how to register a interface with two implementation ? Commented Sep 25, 2018 at 6:30
  • 1
    What you are asking for is impossible and doesn't even make sense. Perhaps you should try explaining WHY you want to do this. Commented Sep 25, 2018 at 7:42

3 Answers 3

2

There are a few options.

First, you can abandon Auto-Wiring and make the registration using a delegate, as follows:

services.AddScoped<TestDIClass1>();    
services.AddScoped<TestDIClass2>();

services.AddTransient<HomeController>(c => new HomeController(
    _td: c.GetRequiredService<TestDIClass2>());   

Here you register both ITestDI by their concrete type. This allows the delegate for HomeController to request a specific implementation using GetRequiredService<T>.

Hand-wiring such object, however, can become cumbersome, especially when HomeController contains more dependencies, because it would require you to resolve all dependencies manually. So instead, you can make use of MS.DI's ActivatorUtilities.CreateInstance class. It implements a simplified form of MS.DI's Auto-Wiring abilities. The following snippet shows this:

services.AddScoped<TestDIClass1>();    
services.AddScoped<TestDIClass2>();

services.AddTransient<HomeController>(c =>
    ActivatorUtilities.CreateInstance<HomeController>(
        c,                                             
        new object[]                                   
        {                                              
            c.GetRequiredService<TestDIClass2>(),         
        }));

In this example, HomeController is requested from ActivatorUtilities. The CreateInstance call is supplied an IServiceProvider instance (the c argument) and an array of instances to inject into HomeController's constructor. ActivatorUtilities will match the supplied objects to HomeController's constructor arguments and will resolve all missing arguments from the provided IServiceProvider.

The use of ActivatorUtilities.CreateInstance allows your registration to stay unchanged, even if new arguments are added to HomeController's constructor, for instance:

public HomeController(
    ILogger logger,          // new argument added
    ITestDI td,
    IProductService service  // another argument added
) 

ActivatorUtilities.CreateInstance will figure out that the resolved TestDIClass2 can be mapped to the td constructor argument, and it will resolve ILogger and IProductService from the IServiceProvider.

There are other options available, but these are in your case probably the most likely options, in case you don't want to change your design.

This information is an condensed version of the book Dependency Injection Principles. Practices, and Patterns. That contains a complete chapter on Microsoft.Extensions.DependencyInjection (MS.DI) and about 16 pages on Working with multiple components.

Sign up to request clarification or add additional context in comments.

Comments

0

It is possible to register concrete class.

public HomeController(TestDIClass2 _td)
{
    this.td = _td; //
}

Comments

0

Since you have registered more than one instances, you have to ask Dependency Injection framework for an IEnumerable<ITestDI> _testDis;

and then you have to iterate trough it

foreach(var instance in _testDis)
{
    instance.YourMethod();
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.