是否有温莎扩展点或通用 IoC 技术来处理一个根下许多场景所需的原始依赖项

本文关键字:许多场 一个 依赖 原始 扩展 处理 技术 IoC 是否 | 更新日期: 2023-09-27 18:17:38

Background

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

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 .我的IControllerFactory是组合根(不包括引导程序(,并在运行时使用IWindsorContainer.Resolve<T>()为作业激活正确的控制器。

所以我想整个事情在引导时看起来像这样:

Global.Asax => ControllerBuilder.Current => MyControllerFactory : IControllerFactory

这在请求时间:

IControllerFactory => IImportantController => IImportantRepository => IOracleConnection

赋予动机

现在,在一个应用程序中,IOracleConnection(以及构建它的基础字符串(可能在以下情况下已知:

  • 应用程序已设计(用于测试的虚构连接字符串(
  • 编译应用程序(完全由生成变量确定的连接字符串(
  • 应用程序已部署(在web.config转换中设置的连接字符串(
  • 应用程序已引导(在配置源中设置的连接字符串只读一次(
  • 应用程序处理某种类型的请求(例如,"通常"但不是"总是"您希望显示来自ProductionDb的数据(
  • 应用程序处理连接参数化的请求(管理ChicagoDb中的Important内容而不是NewYorkDb的请求,或其他任何请求(
  • 应用程序处理会话或用户详细信息完全确定连接的请求(根据设计,10% 的用户居住在Db1上,其余用户居住在Db2上(

问题

您将如何在不违反 DRY 的情况下编写可维护的代码,从而同时实现同一图形的相同依赖项的所有这些潜在用途?

是否有温莎扩展点或通用 IoC 技术来处理一个根下许多场景所需的原始依赖项

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

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

但是,在处理基元时存在一些挑战:

  1. 我认为将原语与容器中的复杂类型区分开来的一件事是目的(服务标识(的概念。就容器而言,"连接字符串"只是一个字符串。它不是连接字符串、IP 地址或主机名 - 它只是一个字符串。
  2. 撇开自定义解析程序的可能性不谈,连接基元以满足其他组件的依赖关系要求的主要方法是在注册时命名基元,然后在注册依赖组件时引用该名称。但是,使用名称会强加耦合(至少在注册方面(,这通常不是必需的。

因此,我宁愿从不注册或依赖原语。我总是将它们包装在一个界面中(通常以"...设置'(,如下所示:

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

这个相对简单的包装器现在为基元提供了其用途的指示。在开发需要这些基元的组件时,它很有帮助,因为我现在可以通过组件构造函数清楚地传达我期望的连接类型(MSSQL 存储库组件根本不接受与 Oracle 数据库的连接字符串(。它还有助于注册,非常清楚地说明依赖关系是什么。

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

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