4

我正在创建一个HttpClient的实例,用于我的Web应用程序正在与之通信的每个不同的API。使用依赖注入管理HttpClient的多个实例

我想使用依赖注入SimpleInjectorHttpClient注入业务类。例如,我有ITwitterBusinessIInstagramBusiness,并且他们两人都在其构造函数中接受HttpClient

当使用依赖注入来注册同一类型的多个对象时,最佳实践是什么?

我很确定问题的一部分可能是我的设计,但这里有一些想法。

我的第一个想法是在DI注册

container.Register<ITwitterBusiness>(() => new TwitterBusiness(httpClientTwitter)); 

似乎很简单的使用委托,但我不知道这种方法有任何不良的副作用,例如通过使SimpleInjector运行速度较慢或者如果我打破了一些设计模式。

我的第二个想法是使用基于上下文注入http://simpleinjector.readthedocs.io/en/latest/advanced.html#context-based-injection

我相信这会让我注入了一定HttpClient的实例,以某一类。仍然不完全确定这是如何工作的。

我很好奇,如果我可以纯粹的设计解决这个问题。例如通过创建虚拟类。我只是没有找到任何好的例子,但如果我理解正确的话,我可以创建虚拟类,如HttpClientTwitter,它继承HttpClient,这样我就可以摆脱模糊的注册。

谢谢!

回答

3

我的第一个想法是在DI注册中使用委托。似乎很简单,但我不知道这种方法是否有任何不良的副作用,例如通过使SimpleInjector运行速度较慢或如果我打破某种设计模式。

如果类型中有任何需要连接的应用程序组件,建议使用自动布线(与注册委托相反)。自动布线简化了注册,并允许简单注射器分析对象图。你的情况似乎都没有关系。 HttpClient不是应用程序组件,而是基础结构类型。似乎没有其他依赖关系,因此注册代理不会造成任何可维护性问题。

与使用自动布线相比,委托注册较慢,Simple Injector无法使用代表进行优化。然而,当你这样做的时候,你不会注意到任何性能上的不同。这不是你应该担心的。

我的第二个想法是使用基于上下文的注入。我相信这将允许我将某个HttpClient实例注入某个类。仍然不完全确定这是如何工作的。

您可以根据上下文进行不同的注册。例如:

var httpClientTwitterRegistration = Lifestyle.Transient.CreateRegistration<HttpClient>(
    () => new HttpClient("https://twitter"), 
    container); 

container.RegisterConditional(typeof(HttpClient), httpClientTwitterRegistration, 
    c => c.Consumer.ImplementationType == typeof(TwitterBusiness)); 

var httpClientInstagramRegistration = Lifestyle.Transient.CreateRegistration<HttpClient>(
    () => new HttpClient("https://instagram"), 
    container); 

container.RegisterConditional(typeof(HttpClient), httpClientInstagramRegistration, 
    c => c.Consumer.ImplementationType == typeof(InstagramBusiness)); 

我很好奇,如果我能解决这个纯粹的设计

通过注入HttpClientTwitterBusiness类,你得到的灵活性虚假的安全感。你似乎可以实现两个交换实现,但由于HttpClient是一个具体类型,所以更改实现是没有意义的。由于TwitterBusiness直接与HttpClient进行通信,因此应将其作为实施细节。换句话说,在TwitterBusiness内移动HttpContext的创建。您需要配置的任何参数(可能是url)都可以注入到TwitterBusiness中。这种方式TwitterBusiness是在完全控制HttpClient的创建和处置,你注入唯一有趣的变化(网址)。

+1

谢谢史蒂文。我忘了提及所有的HttpClient实例都是单例,所以Twitter HttpClient实例将在应用程序的生命周期中重用。我总是倾向于避免静态变量,但我想我可以将它作为TwitterBusiness中的一个静态变量,并且只在构造函数中为null时初始化它。你怎么看?对于单元测试,我可以将HttpMessageHandler传递给构造函数。 – raRaRa

+0

@raRaRa你甚至可以注册你的TwitterBusiness作为单身人士,以防它无状态(通常应该)。这意味着你的httpclient仍然可以是一个实例变量,但我不认为HttpClient是线程安全的。因此,在每一个内部创建它都是最安全的。 – Steven

+0

它是线程安全的,应尽可能重用。这就是为什么我希望它是单身人士。否则,我只需在TwitterBusiness上按需创建HttpClient的实例。但你绝对是对的,TwitterBusiness可以注册为单身人士。我会考虑通过并尝试提出一个决定。谢谢您的帮助! :) – raRaRa