10

我正在使用 Simple Injector,但也许我需要的是更多概念性的答案。

这是交易,假设我的应用程序设置有一个界面:

public interface IApplicationSettings
{
    bool EnableLogging { get; }
    bool CopyLocal { get; }
    string ServerName { get; }
}

然后,通常会有一个实现 IApplicationSettings 的类,从指定的源获取每个字段,例如:

public class AppConfigSettings : IApplicationSettings
{
    private bool? enableLogging;
    public bool EnableLogging
    {
        get
        {
            if (enableLogging == null)
            {
                enableLogging = Convert.ToBoolean(ConfigurationManager.AppSettings["EnableLogging"];
            }
            return enableLogging;
        }
    }
    ...
}

然而!假设我想EnableLogging从 app.config、CopyLocal数据库和ServerName另一个获取当前计算机名称的实现中获取。我希望能够混合匹配我的应用程序配置,而不必创建 9 个实现,每个组合一个。

我假设我不能传递任何参数,因为接口是由注入器(容器)解析的。

我一开始是这么想的:

public interface IApplicationSettings<TEnableLogging,TCopyLocal,TServerName>
where TEnableLogging : IGetValue<bool>
where TCopyLocal : IGetValue<bool>
where TServerName : IGetValue<string>
{
    TEnableLogging EnableLog{get;}
    TCopyLocal CopyLocal{get;}
    TServerName ServerName{get;}
}

public class ApplicationSettings<TEnableLogging,TCopyLocal,TServerName>
{
    private bool? enableLogging;
    public bool EnableLogging
    {
        get
        {
            if (enableLogging == null)
            {
                enableLogging = Container.GetInstance<TEnableLogging>().Value
            }
            return enableLogging;
        }
    }
}

但是,有了这个我有一个主要问题:我怎么知道如何创建一个TEnableLogging(它是 a IGetValue<bool>)的实例?哦,假设这IGetValue<bool>是一个具有 Value 属性的接口,它将由具体类实现。但是具体的类可能需要一些细节(比如 app.config 中的键名是什么)或不需要(我可能只是想始终返回 true)。

我对依赖注入比较陌生,所以也许我的想法是错误的。有没有人对如何做到这一点有任何想法?

(你可以用另一个 DI 库来回答,我不介意。我想我只需要抓住它的概念。)

4

1 回答 1

17

你肯定在这里走错路了。

几年前,我构建了一个应用程序,其中包含一个与您的IApplicationSettings. 我相信我将它命名为它IApplicationConfiguration,但它也包含所有应用程序的配置值。

虽然一开始它帮助我使我的应用程序可测试,但一段时间后,设计开始妨碍我。许多实现都依赖于该接口,但它不断变化,实现和测试版本也随之改变。

就像你一样,我实现了一些延迟加载,但这有一个可怕的缺点。当其中一个配置值丢失时,我才发现它是在第一次调用该值时发生的。这导致了难以验证的配置。

我花了几次重构迭代来找出问题的核心是什么。大接口是个问题。我的IApplicationConfiguration课违反了接口隔离原则,结果是可维护性差。

最后我发现这个界面完全没用。除了违反 ISP 之外,这些配置值描述了实现细节,而不是进行应用程序范围的抽象,最好直接为每个实现提供它们需要的配置值,并且只提供它们需要的值。

当你这样做时,最简单的做法是将这些值包装到一个参数对象中(即使它只是一个值),并将这些配置值注入到构造函数中。这是一个例子:

var enableLogging =
    Convert.ToBoolean(ConfigurationManager.AppSettings["EnableLogging"]);

container.RegisterSingleton(new LoggerSettings(loggingEnabled: enableLogging));

在这种情况下,LoggerSettings是特定于 的配置对象Logger,它需要它作为构造函数参数。

执行此操作时,该enableLogging值仅从配置文件中读取一次,并在应用程序启动期间完成。这使它变得快速并在缺少值时在应用程序启动时失败。

于 2013-03-11T18:41:52.063 回答