C# 配置设置和IoC

C# 配置设置和IoC,c#,.net,dependency-injection,architecture,inversion-of-control,C#,.net,Dependency Injection,Architecture,Inversion Of Control,我使用IoC(DI)方法,通常有参数,由最低层(DB层等)从配置设置(即连接字符串、静态值等)读取。最好的方法是什么 在最底层直接读取,即: string sendGridApiKey = ConfigurationManager.AppSettings["SendGridApiKey"]; 它可以工作,但还需要将该键添加到单元测试项目的配置文件中。此外,程序集取决于配置文件 在最高层(即web应用程序)读取它,并作为参数从所有层抛出?它会起作用,但所有中间层都会得到未使用的参数(因此,它们

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

  • 在最底层直接读取,即:

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

  • 在最高层(即web应用程序)读取它,并作为参数从所有层抛出?它会起作用,但所有中间层都会得到未使用的参数(因此,它们将取决于未使用的东西)
    当最低层的不同实现可能需要不同的参数时,也存在一个问题。也就是说,SendMail1可能需要SMTP/login/password,但SendMail2可能只需要ApiKey,但SendMail1和SendMail2应该实现相同的接口。因此,使用方法#2会带来困难。选项1一开始是一个简单的解决方案,但很快就难以进行测试,需要参考,打破了从最高层到最低层的流动模式等

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

    即使您必须在所有层之间传递它,您的DI引擎也应该在自动链接解析方面为您提供帮助

    e、 g

    如果控制器需要实例化业务层类,需要实例化存储库类,需要连接类,需要设置值,则无需在3个位置手动执行

    您可以在DI引擎中分别定义BL类、存储库类和连接类的注册,它将负责为您实例化控制器

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

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


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

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

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

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

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

      // constructor: publc ConcreteServiceX(IConcreteServiceXSettings settings)...
      container.RegisterType<IService,ConcreteServiceX>();
      container.RegisterInstance<IConcreteServiceXSettings>(
           new ConcreteServiceXSettings(42, ReadSetting("X"));
      
      //构造函数:publc-ConcreteServiceX(IConcreteServiceXSettings设置)。。。
      container.RegisterType();
      容器.RegisterInstance(
      新的ConcreteServicesX设置(42,读取设置(“X”);
      
    这两种方法都将配置系统的知识本地化到一个地方(容器配置),并允许更容易地对每个服务进行单元测试(不依赖于配置存储的类型)以及更高级别的对象(不需要知道服务的任何设置)



    注意:示例使用统一语法,采用您选择的容器

    谢谢您的回答。我在问题中又添加了一段。请阅读please@OlegSh请添加SendMail1和Sendmail2的定义以及接口。向具体类添加特定参数通常很容易。构造函数可以注入sSpecific Paramaters这通常是DI/IoC策略的一个很好的答案,这样您就不会紧密地耦合各种组件。但是这些组件本身应该负责从设置中获取配置(或者不管您如何处理配置设置)。它们可以有重载来接受各种参数,但前提是SendMail1和SendMail2具有相同数量的设置。可能sendmail one是active directory,需要域,而sendmail 2需要其他身份验证方法(用户名//密码//令牌)@RajaNadar你建议在最底层的每个实现的构造函数中添加参数?嗯,那么我们就不需要把这些参数扔到所有级别…@OlegSh如果你通过接口设置依赖项,那么DI就可以利用它所需要的所有参数来具体实例化,而不用担心传递参数如我所说,如果您仍然有问题,请发布您的SendMail代码,我们可以进一步提供帮助