Search code examples
.net-coredependency-injectionvisual-studio-2019

.NET Core Dependency Injection giving "Ambiguous Constructor" errors after update to version 5.0.0


I have a number of classes that consume API services and whose constructors follow the pattern below:

public class SomeClass
{
    public SomeClass(IHttpClientFactory factory, ILogger<SomeClass> logger = null)
    {
        // ...
    }
        
    public SomeClass(HttpClient client, ILogger<SomeClass> logger = null)
    {
        // ...
    }
}

The first constructor is the one used by .NET Core's built-in dependency injection (by calling IServicesCollection.AddHttpClient() from the Startup.ConfigureServices() method, and then calling IHttpClientFactory.CreateClient() in the constructor); the second constructor is mostly used for unit testing, to allow a HttpClient (or mock) to be passed in. This has worked fine until now, but this morning I started getting errors like the one below:

An unhandled exception has occurred

System.InvalidOperationException: Unable to activate type 'Service'. The following constructors are ambiguous:Void .ctor(System.Net.Http.IHttpClientFactory, Microsoft.Extensions.Logging.ILogger`1[Service])Void .ctor(System.Net.Http.HttpClient, Microsoft.Extensions.Logging.ILogger`1[Service]) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(ResultCache lifetime, Type serviceType, Type implementationType, CallSiteChain callSiteChain)

... long stack trace elided...

This was working up till now. It seems strange that the dependency injection mechanism considers the constructors to be ambiguous when IHttpClientFactory and HttpClient do not share any common interfaces.

I updated both Visual Studio 19 to the latest version (16.8.2) and all NuGet packages in the solution at the weekend. The Microsoft.Extensions.DependencyInjection package is now version 5.0.0; previously it was version 3.1.9. I have worked around the issue for now by removing the constructors that take the HttpClient argument and replaced the code in my tests to use a mock IHttpClientFactory instead, so the actual impact was low. However, it would help my peace of mind, if nothing else, to understand why this broke in the first place. Is there some aspect of configuring .NET Core dependency injection that I have overlooked? Or has the strategy to resolve dependencies changed subtly between versions 3.1.9 and 5.0.0?


Solution

  • I faced this error on .net 7. But after reading Microsoft blog about .Net Dependency Injection, it got me to understand.

    For my workground is that I need to instantiate my logic with both Dependency Injection and Dependency Inversion or we could call it hybrid. This way I can use my logic for both long-running task and normal HttpRequest thread rather than duplicate logic separately.

    Update

    As Ken Karen demonstrated to the right point in the comment is that "The constructor with the most parameters where the types are DI-resolvable is selected".

    We have to accept both parameters of constructors to avoid ambiguity.

        public class SomeClass: ISomeClass
        {
           //constructor that we intend to inject using DI
           //The constructor with the most parameters, going to be selected
           public SomeClass(HttpClient client, IHttpClientFactory factory, ILogger<SomeClass> logger = null)
           {
               // ...
           }
           //construtor that we intend to instantiate using DPI
           public SomeClass(HttpClient client, ILogger<SomeClass> logger = null)
           {
               // ...
           }
        }
    

    OtherClass.cs

       public class OtherClass
       {
          private readonly SomeClass someClass;
          private readonly ISomeClass _someClass;
    
          //using DI to get "SomeClass" instance
          public OtherClass(ISomeClass iSomeClass)
          {
             _someClass = iSomeClass;
             //dependency inversion (DPI)
             someClass = new SomeClass(httpClient, logger);
          }
    
       }
    

    Maybe your problem has been resolved for long time ago, but I hope it would be helpful for solution finders with similar situation.