如何使用依赖注入从多个源获取配置?

时间:2013-03-11 18:12:42

标签: c# .net configuration dependency-injection simple-injector

我正在使用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;
        }
    }
    ...
}

HOWEVER!假设我想从app.config获取EnableLogging,从数据库获取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的实例(这是IGetValue<bool>)?哦,假设IGetValue<bool>是一个具有Value属性的接口,它将由具体类实现。但具体类可能需要一些细节(比如app.config中键的名称)或不需要(我可能只想返回总是如此)。

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

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

1 个答案:

答案 0 :(得分:15)

你肯定会走错路。

几年前,我构建了一个包含界面的应用程序,就像你的IApplicationSettings一样。我相信我将其命名为IApplicationConfiguration,但它也包含所有应用程序的配置值。

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

就像你我实施了一些延迟加载,但这有一个可怕的不利方面。当其中一个配置值丢失时,我只发现它是在第一次调用该值时执行的。这导致配置难以验证。

我花了几次重构来找出问题的核心是什么。大接口是一个问题。我的IApplicationConfiguration课程违反了Interface Segregation Principle,结果是可维护性差。

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

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

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

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

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

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