1

I have an interface

public interface ITest
{
    void SayHello();
}

And it's implemented as

public class Test : ITest
{
    Test(string type)
    {
        // Do Something with type
    }

    public void SayHello()
    {
        // 
    }
}

Register it in the DI as:

services.AddScoped<ITest, Test>();

I need to create two instance with these conditions:

Test t1 = new Test("ABC");
Test t2 = new Test("XYZ");

How I can achieve this using DI?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Sooraj Bathery
  • 135
  • 3
  • 10
  • https://medium.com/null-exception/factory-pattern-using-built-in-dependency-injection-of-asp-net-core-f91bd3b58665 – Chetan Jun 26 '22 at 04:56
  • 2
    This completely depends on the meaning of the strings `ABC` and `XYZ` in your application and where they are coming from. Are they coming from the config file, and thus constant for the duration of the running application, and there just two `Test` 'registrations'? Or represent these two strings runtime data, coming from a database, or web request, and do you have other `Test` instances at runtime? Please update your post with answers to this question. Based on this information we can supply you with a good solution. – Steven Jun 26 '22 at 10:43

2 Answers2

3

With this exact use-case in mind, DI provides the following method: ActivatorUtilities.CreateInstance
(note the use of ITest vs Test)

Test t1 = 
    ActivatorUtilities
        .CreateInstance<Test>("ABC");

Test t2 = 
    ActivatorUtilities
        .CreateInstance<Test>("XYZ");
Steven
  • 166,672
  • 24
  • 332
  • 435
Shai Cohen
  • 6,074
  • 4
  • 31
  • 54
3

There are several options how you can implement it.

1. Factory

Create a factory for generating the instance you need:

public TestFactory : ITestFactory
{
   public ITest Create(string type)
   {
       return new Test(type);
   }
}

And register just the factory

services.AddScoped<ITestFactory, TestFactory>();

Advantage you have the single piece of code the ITestFactory which can be mocked and the whole logic how the instance and which instance is create resides there.

2. List of implementations

Register them all as ITest

services.AddScoped<ITest>(sp => new Test("ABC"));
services.AddScoped<ITest>(sp => new Test("XYZ"));

... and then use it in a class like this:

public class Consumer 
{
   public Consumer(IEnumerable<ITest> testInstances)
   {
       // This is working great if it does not matter what the instance is and you just need to use all of them.
   }
}

3. ActivatorUtilities

... or just use the ActivatorUtilities which is a way to create the instance directly from a IServiceProvider see Documentation or this other SO question

var serviceProvider = <yourserviceprovider>;
var test1 = ActivatorUtilities.CreateInstance<Test>(sp, "ABC");
var test2 = ActivatorUtilities.CreateInstance<Test>(sp, "XYZ");

But like it is also written in the comment of your question it really depends what you want to do with both instances and where do you get the string type from.

You can also do more advanced ways with a combination of option 1 and 2 but therefore the better description of your problem and usage is needed.

Martin
  • 3,096
  • 1
  • 26
  • 46
  • What if Test aslo have ILogger to be injected as parameter? How do you inject it into for example 2. List of implementations? – exSnake Jan 31 '23 at 00:10
  • 1
    you can use: ```services.AddScoped(sp => new Test("ABC", sp.GetService()));``` to accomplish this if needed – Martin Feb 01 '23 at 03:08