将运行时构造函数参数传递给Autofac的最佳方法是什么?

问题描述:

我建立一个通用的中间件软件(窗口服务),将执行许多jobs的..如:将运行时构造函数参数传递给Autofac的最佳方法是什么?

  • 同步库存
  • 进口订单

我我正在使用C#与Windows Service以及Quartz.NET来安排作业运行。 我也在使用AutoFac。

从我所了解的情况来看,应该在组合根处构建AutoFac依赖关系。这很好..但是,我有某些服务被注入需要运行时参数(来自数据库的配置值)。

例如:

  • 某些作业将连接到SFTP服务器由此连接细节存储在数据库中的密钥值对
  • 一些作业将连接到远程API,由此那些API认证细节是针对数据库中的作业存储的。

我已经做了这方面的一些研究和一些替代品基本上建议取消此类服务的构造(如SFTP客户端),并通过这些作为配置的方法..

取而代之的是客户端有你在通过这些构造,如

SftpClient(string host, string username, string password, int timeoutInSeconds)

这将有一个默认的构造函数,并且配置方法。

我完全不喜欢这个,它违背了我所学到的,因为你应该尝试配置你的对象,使它通过构造函数处于一致的状态。

什么是最佳选择? 我的JobFactory方法目前取决于IComponentContext。 我见过有办法将参数传递给AutoFac来构造对象,但是我读过的东西表明它并不理想。 使用我的工厂更好吗

最简单的解决方案是注入工厂而不是实例。不要注入AutoFac容器本身(隐藏依赖关系)。只要写一个简单的课程。

class SftpClientFactory : ISftpClientFactory 
{ 
    public SftpClient GetClient(string host, string userName, string password, int timeout) 
    { 
     return new SftpClient(host, userName, password, timeout); 
    } 
} 

然后像这样注册它:

container.RegisterType<ISftpFactory, SftpFactory>(); 

而在你的类

class Example 
{ 
    private readonly ISftpClientFactory _clientFactory; 

    public Example(ISftpClientFactory injectedFactory) 
    { 
     _clientFactory = injectedFactory; 
    } 

    public void DoTheWork() 
    { 
     var client = _clientFactory.GetClient(host, userName, password, timeout); 
    } 
} 

作为奖励,你现在有对象的生命周期的完全控制,这听起来像它对于一个sftp客户端来说可能很重要,该客户端可能会拥有一个非管理资源并且可能是Disposable

public void DoTheWork() 
    { 
     using (var client = _clientFactory.GetClient(host, userName, password, timeout)) 
     { 
      client.DownloadFile(); 
     } 
    } 

这个方案保留了控制的反转,你仍然保留一个单一的合成根,客户端仍然可以被你的单元测试项目截断,而且你仍然可以获得编译时解析依赖关系。唯一的缺点是写存根的工作稍微多一点,因为你也必须写一个存根工厂。

+0

谢谢 - 我假设Factory类与客户端本身位于同一个命名空间中?比如,我有一个单独的“公共”项目,这些公共服务和客户都住在这里 - 他们会进入该命名空间而不是消费者权利的名称空间? – Lock

+0

正确。工厂随着它创建的课程而去。消费者不应该知道或关心实例来自哪里,只要它符合接口。 –

+0

我没有看到如何在嵌套依赖关系时使用此方法。举例来说,ISmtpClient对IDependancy有依赖性,这也需要一些运行时间值。这将如何工作? – Lock

您可以利用delegate factories在运行时传递自定义参数。在这种情况下,您不必创建任何自定义工厂,并且在您的测试中也可以轻松地进行模拟。

您不能使用内置的Func<X,Y,T>委托,因为您必须传递的参数具有相同的类型。但是你可以介绍你自己的代表。如果您有SmtpClient一个单独的接口,它看起来像这样

public delegate ISmtpClient SmtpClientFactory(string host, string username, string password, int timeout); 

然后,而不是注入ISmtpClient,你应该注入SmptClientFactory。那里不需要额外的注册。

如果您没有ISmtpClient接口,它看起来几乎相同,但稍后测试会更困难。