在构造函数中组合依赖项和值

时间:2013-05-27 11:29:17

标签: c# dependency-injection constructor-injection

我经常遇到一个问题,即一个类需要一个依赖项和一些字符串值来初始化。例如,请考虑以下代码:

public class CustomerService
{
   public CustomerService(ITcpConnection connection, string realm)
   {
   }
}

问题在于,由于附加的字符串参数,从DI容器中取出它并不容易。解决此问题的最佳方法是什么?这些是我能想到的方式:

  1. 初始化方法(它不够好,因为它提供Mark Seeman定义的时间耦合,我同意它不是一个非常好的方法)
  2. 抽象工厂(我真的不需要一个额外的间接层,因为参数是一个配置,我在配置时就知道了)
  3. 在示例中使用并在DI容器中注册类的实例

1 个答案:

答案 0 :(得分:2)

最好避免在同一构造函数中混合原语/配置值和(服务)依赖项。它们使DI配置复杂化,并且经常导致不可读且易碎Composition Root

请执行以下操作:

摘要配置值

当将相同的值注入多个服务时,通常会丢失抽象。一个很好的例子是在所有存储库中注入string connectionString值。在这种情况下,应用程序缺少IConnectionFactory抽象(或相关的东西)。应该将连接字符串注入IConnectionFactory实现(可能只将该连接字符串作为其构造函数参数),而不是将连接字符串注入到许多服务中,并让其他服务依赖于IConnectionFactory而不是string

将配置值移至属性

通过将配置值从构造函数移动到属性,构造函数将只包含可以明确解析的类型,这意味着DI框架可以自动连接该构造函数,而不需要任何特殊的 - 最重要的是脆弱的 - 配置。您当然必须连接该构造函数,但大多数DI容器都有一种简单的方法,可以安全地编译,并且可以验证编译时。以下是Simple Injector的示例:

container.Register<IUnitOfWork, UnitOfWork>();

container.RegisterInitializer<UnitOfWork>(
    instance => instance.ConnectionTimeOut = TimeSpan.FromSeconds(30));

以下是Autofac的示例:

builder.RegisterType<UnitOfWork>() .OnActivating(e => 
    e.Instance.ConnectionTimeOut = TimeSpan.FromSeconds(30));

创建子类型

当属性注入不是一个选项时(例如,因为你不能更改源代码),你可以派生一个放在你的Composition Root中的子类型,并定义正确的构造函数并注册该子类型:

public class MyService : SomeExternalService
{
    public MyService(ISomeDependency dep) : base(dep, "My Config Value") { }
}

// Registration
container.Register<IService, MyService>();