配置设置和IoC

时间:2016-02-21 00:17:09

标签: c# .net dependency-injection architecture inversion-of-control

我使用IoC(DI)方法并且通常具有参数,这些参数是由最低层(DB层等)从配置设置(即连接字符串,静态值等)读取的。最好的方法是什么?

  1. 直接读取最下层,即:

    string sendGridApiKey = ConfigurationManager.AppSettings["SendGridApiKey"];
    
  2. 它可以工作,但还需要将此密钥添加到单元测试项目的配置文件中。此外,程序集取决于配置文件

    1. 在最高层(即网络应用程序)中读取它并从所有图层中作为参数抛出?它会起作用,但是所有中间层都会获得未使用的参数(因此,它们将取决于未使用的内容)。
    2. 当最低层的不同实现可能需要不同的参数时,也存在问题。即SendMail1可能需要SMTP /登录/密码,但SendMail2只需要ApiKey,但SendMail1和SendMail2应该实现相同的接口。因此,它使用方法#2

      会产生困难

2 个答案:

答案 0 :(得分:3)

您所概述的方法都不是很好 - 首先(在服务中读取配置)阻止了您提到的单元测试,其次(从顶层传递配置)需要了解每个服务的所有可能实现顶层。

我喜欢依赖DI容器的方法,包括配置存储和为每个接口注册的对象类型的知识:

  • 在注册时间内传递配置 - 即如果容器支持注册工厂方法,那么工厂方法可以读取配置而不是调用具体服务的特定构造函数

    // constructor: publc ConcreteServiceX(int setting1, string setting2)...
    container.RegisterFactory<IServiceX>(
        container => return new ConcreteServiceX(42, ReadSetting("X"));
    
  • 将容器中每个服务的配置注册为类/接口

    // constructor: publc ConcreteServiceX(IConcreteServiceXSettings settings)...
    container.RegisterType<IService,ConcreteServiceX>();
    container.RegisterInstance<IConcreteServiceXSettings>(
         new ConcreteServiceXSettings(42, ReadSetting("X"));
    

这两种方法都将配置系统的知识本地化到一个地方(容器配置),并允许更容易的单元测试每个服务(不依赖于配置存储的类型)以及更高级别的对象(不需要知道任何服务设置)

注意:示例使用类似Unity的语法,采用您选择的容器

答案 1 :(得分:2)

选项1最初是一个更简单的解决方案,但很快就会很难进行测试,需要引用,打破从最高层到最低层的流动值等模式。

推荐的模式是#2,其中最高层将所有依赖关系及其值发送到较低层。

即使您必须将其传递到所有图层,您的DI引擎也应该在自动链接分辨率方面为您提供帮助。

e.g。

如果你的控制器需要实例化一个业务层类,它需要实例化一个Repository类,它需要一个需要设置值的Connection类,你不需要在3个地方手动完成它。 / p>

您可以在DI引擎中单独定义BL类,Repository类和Connection类的注册,并且它会为您实例化控制器。

看起来可能很乏味,但从长远来看通常会带来很大的好处。 (在明确的合同定义,单元测试,没有反模式,孤立的问题等方面)。

如果你真的担心将它传递给3个地方,那么在工厂和聚合服务方面有各种各样的选择。每个都有自己的优缺点,取决于你正在使用的DI引擎。如果选项2是绝对不可接受的,请告诉我们。

e.g。 Autofac允许您将许多构造函数参数包装到Single Aggregate服务接口中,以便Autofac可以为您注入。