【问题标题】:configuration settings and IoC配置设置和 IoC
【发布时间】:2016-02-21 00:17:09
【问题描述】:

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

  1. 直接在这个最底层读取,即:

    string sendGridApiKey = ConfigurationManager.AppSettings["SendGridApiKey"];
    

它可以工作,但还需要将此密钥添加到单元测试项目的配置文件中。另外,汇编依赖于配置文件

  1. 在最高层(即 Web 应用程序)读取它并作为所有层的参数抛出?它会起作用,但所有中间层都会获得未使用的参数(因此,它们将取决于未使用的事物)。

当最低层的不同实现可能需要不同的参数时,还有一个问题。 IE。 SendMail1 可以需要 SMTP/登录名/密码,但 SendMail2 可以只需要 ApiKey,但 SendMail1 和 SendMail2 应该实现相同的接口。因此,使用方法 #2 会造成困难

【问题讨论】:

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


    【解决方案1】:

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

    我喜欢依赖 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 的语法,适用于您选择的容器

    【讨论】:

      【解决方案2】:

      选项 1 开始是一个更简单的解决方案,但很快就难以测试、需要参考、打破围绕从最高层到最低层的流动值的模式等。

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

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

      例如

      如果你的控制器需要实例化一个Business Layer类,需要实例化一个Repository类,需要一个Connection类,需要设置值,3个地方都不需要手动做。

      您可以在 DI 引擎中分别定义注册 BL 类、Repository 类和 Connection 类,它会负责为您实例化一个控制器。

      这可能看起来很乏味,但从长远来看通常有很大的好处。 (在清晰的合约定义、单元测试、无反模式、孤立的关注点等方面)

      如果您真的担心通过 3 个地方,那么在工厂和聚合服务方面有多种选择。每个都有其优点/缺点,并取决于您使用的 DI 引擎。让我们知道选项 2 是否绝对不可接受。

      例如Autofac 允许您将大量构造函数参数包装到单个聚合服务接口中,以便 Autofac 可以为您注入。

      【讨论】:

      • 感谢您的回复。我在我的问题中又添加了一段。请阅读
      • @OlegSh 请添加 SendMail1 和 2 的定义,以及接口。通常很容易将特定参数添加到具体类中。构造函数可以注入特定参数
      • 这通常是 DI/IoC 策略的一个很好的答案,这样您就不会紧密耦合各种组件。但是这些组件本身应该负责从设置中获取配置(或者您处理配置设置)。它们可能有重载来接受各种参数,但这假设 SendMail1 和 SendMail2 具有相同数量的设置。也许 sendmail 1 是活动目录,需要一个域,而 sendmail 2 需要其他一些身份验证方法(用户名//密码//令牌)
      • @RajaNadar 您建议向最低层的每个实现的构造函数添加参数?嗯,那我们就不用把这些参数扔到各个层级了……
      • @OlegSh 如果您通过接口设置依赖项,那么 DI 可以使用它需要的所有参数进行具体实例化,而无需担心在所有层之间传递所有值。正如我所说,如果您仍有疑虑,请发布您的 SendMail 代码,我们可以提供进一步的帮助
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-09
      • 2023-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多