我有许多使用 API 服务的类,其构造函数遵循以下模式:
public class SomeClass
{
public SomeClass(IHttpClientFactory factory, ILogger<SomeClass> logger = null)
{
// ...
}
public SomeClass(HttpClient client, ILogger<SomeClass> logger = null)
{
// ...
}
}
第一个构造函数是 .NET Core 内置依赖注入使用的构造函数(通过从
IServicesCollection.AddHttpClient()
方法调用 Startup.ConfigureServices()
,然后在构造函数中调用 IHttpClientFactory.CreateClient()
);第二个构造函数主要用于单元测试,以允许传入 HttpClient
(或模拟)。到目前为止,这一直运行良好,但今天早上我开始收到如下错误:
发生了未处理的异常
系统.InvalidOperationException: 无法激活类型 '服务'。 以下构造函数是不明确的:Void .ctor(System.Net.Http.IHttpClientFactory, Microsoft.Extensions.Logging.ILogger`1[服务])无效 .ctor(System.Net.Http.HttpClient, Microsoft.Extensions.Logging.ILogger`1[服务]) 在 Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(ResultCache 生命周期、类型服务类型、类型实现类型、CallSiteChain 呼叫站点链)
...长堆栈跟踪被删除...
到目前为止,这一直有效。当
IHttpClientFactory
和 HttpClient
不共享任何公共接口时,依赖注入机制认为构造函数是不明确的,这似乎很奇怪。
我周末将 Visual Studio 19 更新到最新版本 (16.8.2) 以及解决方案中的所有 NuGet 包。
Microsoft.Extensions.DependencyInjection
软件包现在版本为 5.0.0;之前版本是 3.1.9。我现在已经解决了这个问题,删除了采用 HttpClient
参数的构造函数,并将测试中的代码替换为使用模拟 IHttpClientFactory
,因此实际影响很小。然而,如果没有别的办法的话,了解一下为什么会出现这种情况,会有助于我内心的平静。我是否忽略了配置 .NET Core 依赖注入的某些方面?或者解决依赖关系的策略在 3.1.9 和 5.0.0 版本之间是否发生了微妙的变化?
我在 .net 7 上遇到了这个错误。但是在阅读了有关 .Net 依赖注入的 Microsoft 博客之后,它让我明白了。
对于我的工作来说,我需要使用依赖注入和依赖反转来实例化我的逻辑,或者我们可以将其称为混合。这样我就可以将我的逻辑用于长时间运行的任务和正常的 HttpRequest 线程,而不是单独重复逻辑。
更新
正如Ken Karen在评论中所展示的正确观点是“
The constructor with the most parameters where the types are DI-resolvable is selected
”。
我们必须接受构造函数的两个参数以避免歧义。
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);
}
}
也许你的问题已经解决很久了,但我希望这对有类似情况的解决方案查找者有所帮助。