8

If I have two constructors for a class, how does the service container choose which one to use when I'm registering that service in ConfigureServices?

So lets say I have a class called MyClass with a corresponding interface IMyClass. In the ConfigureServices() method I call the following line of code

services.AddScoped<IMyClass, MyClass>();

How does it choose which constructor to use if I have the following constructors?

MyClass(ILogger logger)

MyClass(ILogger logger, IConfguration configuration)
user3236794
  • 578
  • 1
  • 6
  • 16
  • Cant say for sure but I believe it will use the default without parameter (neither of your constructors). – Jawad Dec 13 '19 at 05:01
  • 4
    It'll use the constructor that can satisfy the most dependencies i.e. bottom one in your case. – JohanP Dec 13 '19 at 05:08
  • https://stackoverflow.com/a/39094772/2401021 - the behaviour is to use the first matching constructor where the parameters are known – majita Dec 13 '19 at 05:20

2 Answers2

11

The constructor matching is performed by a method called CallSiteFactory:CreateConstructorCallSite. Based on its source code, the algorithm is the following:

  • Find all public constructors of the target type
    • If there's none, throw an exception
    • If there's only one, use it
  • Sort all constructors by their number of arguments (ctors with most arguments first)
    • Select the one with the most number of parameters that can be injected by DI
    • If there are multiple such ctors, throw an exception
  • If there are no constructor that can be used, throw an exception

To clarify when there can be an ambiguity, consider these ctors:

MyClass(ILogger logger)
MyClass(IConfguration configuration)

The DI engine can't decide which one to use, because both have valid parameters that can be injected.

In the following case however, there's no ambiguity because the int type isn't registered in the DI engine, and thus can't be injected via DI, and thus the first ctor will be chosen:

MyClass(ILogger logger)
MyClass(int i)

So to answer your question: in your case, the second constructor will be used.

Métoule
  • 13,062
  • 2
  • 56
  • 84
0

I tested it out with the following cases;

  1. 1 with an interface (registered), 1 with an interface and a primitive parameter (not registered)

    public Application(ITestClass testClass)

    public Application(ITestClass testClass, string message)

It choses the first one.

  1. 1 with an interface (registered), 1 with two interfaces (registered)

    public Application(ITestClass testClass)

    public Application(ITestClass testClass, ITestClass2 testClass2)

It choses the second one.

  1. 1 with an interface (registered), 1 with 2 interfaces (registered) and 1 primitive parameter (not registered)

    public Application(ITestClass testClass)

    public Application(ITestClass testClass, ITestClass2 testClass2, string message)

It choses the first one again.

When I registered the string type, it started to choose the second one.

So the long story short, it will try to find the most comprehensive one with the only known dependencies.

ycansener
  • 341
  • 3
  • 5