通过 StructureMap 定义“HttpClient”单例会导致有关“HttpMessageHandler”未在运行时配置的错误

Posted

技术标签:

【中文标题】通过 StructureMap 定义“HttpClient”单例会导致有关“HttpMessageHandler”未在运行时配置的错误【英文标题】:Defining an 'HttpClient' singleton via StructureMap causes an error about 'HttpMessageHandler' being not configured in runtime 【发布时间】:2019-01-03 14:55:05 【问题描述】:

试图在 StructureMap ala 中定义一个 HttpClient 单例:

For<HttpClient>().Singleton().UseIfNone<HttpClient>();

这会导致运行时出现以下错误(依赖注入时):

   StructureMap.StructureMapConfigurationException: No default Instance is registered and cannot be automatically determined for type 'System.Net.Http.HttpMessageHandler'

   There is no configuration specified for System.Net.Http.HttpMessageHandler

   1.) new HttpClient(*Default of HttpMessageHandler*)
   2.) System.Net.Http.HttpClient
   3.) Instance of System.Net.Http.HttpClient
   4.) new AdmanAdapter(*Default of HttpClient*)
   5.) Organotiki.vNext.PostEval.Data.Adapters.ADMAN.AdmanAdapter
   6.) Instance of [....]

      at lambda_method(Closure , IBuildSession , IContext )
      at StructureMap.Building.BuildPlan.Build(IBuildSession session, IContext context)
      at StructureMap.BuildSession.BuildNewInSession(Type pluginType, Instance instance)
      at StructureMap.Pipeline.NulloTransientCache.Get(Type pluginType, Instance instance, IBuildSession session)
      at StructureMap.BuildSession.ResolveFromLifecycle(Type pluginType, Instance instance)
      at StructureMap.SessionCache.GetObject(Type pluginType, Instance instance, ILifecycle lifecycle)

如果我们也像这样配置HttpMessageHandler:

For<HttpClient>().Singleton().UseIfNone<HttpClient>();
For<HttpMessageHandler>().UseIfNone(x => new HttpClientHandler());

然后问题就消失了。问题是为什么? HttpClient 的默认构造函数负责自己的依赖注入:

/// <summary>Initializes a new instance of the <see cref="T:System.Net.Http.HttpClient" /> class.</summary>
[__DynamicallyInvokable]
public HttpClient()
  : this((HttpMessageHandler) new HttpClientHandler())


我错过了什么吗?

【问题讨论】:

容器默认不使用默认构造函数。如果找到多个构造函数,则使用依赖关系最多的构造函数。 【参考方案1】:

来自http://structuremap.github.io/registration/constructor-selection的结构图文档

如果混凝土上有多个公共构造函数 类,StructureMap 的默认行为是选择“最贪婪的” 构造函数,即参数最多的构造函数。

如果你查看HttpClient 的可能构造函数,它应该是

public HttpClient();
public HttpClient(HttpMessageHandler handler);
public HttpClient(HttpMessageHandler handler, bool disposeHandler);

【讨论】:

感谢您的洞察力。还有一个问题:如果我使用“For().Singleton().UseIfNone(x => new HttpClient());”尽管我已经明确指定了构造函数,但我仍然得到同样的错误。什么给了? @XDS 这看起来不正确。您明确地说,“对于这个 concrete 类,使用它的单个实例。如果没有找到具体实例,则使用这个 lambda/构造函数逻辑”。 HttpClient 是一个具体的类,因此结构映射总是能够“尝试”创建它的实例。如果它是一个接口,例如IFoo,那么它将求助于使用您的 lamdba 表达式来获取实例。就像现在一样,它完全忽略了您的UseIfNone 我想我必须继承 HttpClient 的子类,然后用接口装饰子类,然后配置那个接口。这将是一次多么奇怪的旅行。谢谢顺便说一句。我自己永远无法想象出所有这些微妙之处。竖起大拇指,伙计。 您应该能够执行类似于以下的操作...For&lt;HttpClient&gt;().Singleton().Use(x =&gt; new HttpClient())。在这里,您明确告诉结构映射“这正是我希望创建 HttpClient 实例的方式”【参考方案2】:

扩展@Brad M 的答案,对我有用的是.SelectConstructor(() =&gt; new HttpClient())。指定应显式使用的构造函数。

【讨论】:

以上是关于通过 StructureMap 定义“HttpClient”单例会导致有关“HttpMessageHandler”未在运行时配置的错误的主要内容,如果未能解决你的问题,请参考以下文章

告诉 StructureMap 使用特定的构造函数

获取使用StructureMap从基类继承的唯一类型实例

使用 StructureMap 3.* 拦截

DI 依赖注入之StructureMap框架

使用 PetaPoco 与 StructureMap 的共享连接

StructureMap - 能够在运行时替换程序集