我正在构建一个通用的中间件软件(一个Windows服务),它将执行许多jobs
..例如:
我正在使用带有Windows服务的C#和Quartz.NET来安排作业运行。 我也在使用AutoFac。
根据我的理解,应该在组合根目录下构建AutoFac依赖项。这很好..但是,我有一些需要运行时参数的注入服务(配置值来自数据库)。
例如:
我已经对此做了一些研究,一些替代方案基本上建议删除其中一些服务的构造函数(例如SFTP客户端)并将它们作为配置方法传递。
而不是客户端有一个构造函数,如
SftpClient(string host, string username, string password, int timeoutInSeconds)
它将有一个默认构造函数,以及你传递它们的configure方法。
我根本不喜欢这样 - 它违背了我所学到的,你应该尝试配置你的对象,使它通过构造函数处于一致状态。
最好的选择是什么?
我的JobFactory
方法目前依赖于IComponentContext
。
我已经看到有办法将参数传递给AutoFac来构建对象,但我读过的东西表明它并不理想。
是否更好地使用我的工厂
答案 0 :(得分:2)
最简单的解决方案是注入工厂而不是实例。不要注入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();
}
}
此方案保留了控制的反转,您仍然保留单个组合根,客户端仍然可以由您的单元测试项目存根,并且您仍然可以获得依赖项的编译时解析。唯一的缺点是编写存根稍微多一点,因为你也必须编写一个存根工厂。
答案 1 :(得分:0)
您可以利用delegate factories在运行时传递自定义参数。在这种情况下,您不必创建任何自定义工厂,并且在测试中也很容易进行模拟。
您不能使用内置Func<X,Y,T>
委托,因为您必须传递的参数属于同一类型。但是你可以介绍自己的代表。如果您有SmtpClient
的单独界面,它将如下所示
public delegate ISmtpClient SmtpClientFactory(string host, string username, string password, int timeout);
然后,您应该注入ISmtpClient
而不是注入SmptClientFactory
。那里不需要额外的注册。
如果你没有ISmtpClient
界面,它看起来几乎一样,虽然以后测试会更难。