C# 是否有Windsor扩展点或通用IoC技术来处理一个根目录下的许多场景中所需的原始依赖关系? 背景

C# 是否有Windsor扩展点或通用IoC技术来处理一个根目录下的许多场景中所需的原始依赖关系? 背景,c#,inversion-of-control,naming-conventions,castle-windsor,compositionroot,C#,Inversion Of Control,Naming Conventions,Castle Windsor,Compositionroot,我有许多服务是由依赖于要创建的连接字符串的组件实现的,例如: public interface IImportantRepository { ... } public class ImportantRepository { public ImportantRepository(IOracleConnection connection) { ... } public ImportantRepository(string connectionString) { ... } // r

我有许多服务是由依赖于要创建的连接字符串的组件实现的,例如:

public interface IImportantRepository { ... }

public class ImportantRepository
{
    public ImportantRepository(IOracleConnection connection) { ... }
    public ImportantRepository(string connectionString) { ... } // rarely my constructor of choice, but included for clarity
}
我通常更喜欢第一个构造函数而不是第二个构造函数,因为为了使用连接字符串,我必须用它创建一个连接,这反过来需要使用
string
依赖项解析
IOracleConnection

我的
IImportantController
有一个构造函数和参数
IImportantRepository
。My
IControllerFactory
是合成根目录(不包括引导程序),并在运行时使用
IWindsorContainer.Resolve()
为作业激活正确的控制器

所以我想整个过程在引导时是这样的:

Global.Asax => ControllerBuilder.Current => MyControllerFactory : IControllerFactory
在请求时:

IControllerFactory => IImportantController => IImportantRepository => IOracleConnection
动机 现在,在一个应用程序中,
IOracleConnection
(以及构建它的底层字符串)可能在以下情况下已知:

  • 设计应用程序(用于测试的虚拟连接字符串)
  • 编译应用程序(完全由构建变量确定的连接字符串)
  • 部署应用程序(在
    web.config
    转换中设置的连接字符串)
  • 应用程序已引导(配置源中设置的连接字符串只读一次)
  • 应用程序处理特定类型的请求(例如,“通常”但不是“始终”您希望显示来自
    ProductionDb的数据)
  • 应用程序处理连接参数化的请求(管理
    ChicagoDb
    中的
    重要
    内容的请求,而不是
    NewYorkDb
    或其他任何内容的请求)
  • 应用程序处理一个请求,其中会话或用户详细信息完全决定了连接(10%的用户生活在
    Db1
    上,其余的用户生活在
    Db2
    上,按设计)
问题:
如何编写可维护的代码而不违反DRY,从而同时实现同一图形中相同依赖项的所有这些潜在用途?

原语与其他组件可能依赖并在容器中注册的任何其他类型非常相似。它们可以命名,可以从一些静态上下文(例如配置文件、环境变量或当前用户会话)中解析,等等

您列出的场景都可以应用于复杂类型或基本类型,其中大多数都有干净的解决方案

但是,在处理原语时有两个挑战:

  • 我认为原语与容器中复杂类型的区别在于目的(服务标识)的概念。就容器而言,“连接字符串”只是一个字符串。它不是连接字符串、IP地址或主机名—它只是一个字符串
  • 撇开自定义解析器的可能性不谈,连接原语以满足其他组件的依赖性要求的主要方法是在注册时命名原语,然后在注册依赖组件时引用该名称。但是,使用名称会产生耦合(至少在注册方面),这通常是不必要的 因此,我宁愿从不注册或依赖原语。我总是将它们包装在一个界面中(通常以“…设置”结尾),如下所示:

    这个相对简单的包装器现在为原语提供了其用途的指示。它有助于开发需要这些原语的组件,因为我现在可以通过组件构造函数清楚地传达我所期望的连接类型(MSSQL存储库组件根本不接受到Oracle数据库的连接字符串)。它也有助于注册,使依赖关系非常清晰

    在实践中,大多数时候,我将这些“设置”对象的构造转移到Krzysztof Koźmic的一个优秀的变体上。(长话短说:它在配置文件的AppSettings设置字典周围提供了一个强类型包装器。)因此,我很少真正编写这些类的实现,更不用说实例化它们了。但是,您当然可以这样做(例如,为测试提供硬编码字符串,或将传递给控制台应用程序的命令行参数形式化)

    将原语包装在一个明确指示其用途的接口中也意味着在许多情况下,您不需要使用名称进行注册。例如,如果只有一个“生产Oracle数据库”,则任何需要
    IOracleProductionDatabaseConnectionSettings
    的组件都将从容器中解析正确的设置,而不需要任何魔术字符串

    public interface IMSSqlDatabaseConnectionSettings 
    {
        string ConnectionString { get; set; }
    }
    
    public interface IOracleDatabaseConnectionSettings 
    {
        string ConnectionString { get; set; }
    }