如何使用依赖注入,获得来自多个来源的配置? [英] How to, using dependency injection, get configuration from multiple sources?

查看:180
本文介绍了如何使用依赖注入,获得来自多个来源的配置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我用简单的注射,但也许我需要的是更多的是概念性的答案。

I'm using Simple Injector, but maybe what I need is more of a conceptual answer.

这里的交易,假设我有一个接口与我的应用程序设置

Here's the deal, suppose I have an interface with my application settings:

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

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

Then, one would usually have a class which implements IApplicationSettings, getting each field from a specified source, for instance:

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 从数据库和服务器名称从中获取当前计算机名称的另一个实现。我想成为能够无需创建9的实现,每个组合的混合,搭配我的应用程序的配置。

HOWEVER! Let's say I want to get EnableLogging from app.config, CopyLocal from database, and ServerName from another implementation which gets the current computer name. I want to be able to mix-match my app configuration without having to create 9 implementations, one for each combination.

我假设我不能传递任何参数,因为接口通过注射器(容器)都解决了。

I'm assuming that I can't pass any parameters because the interfaces are resolved by the injector (container).

我想到了这一点,首先:

I thought of this, initially:

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<布尔> ) ?哦,假设 IGetValue<布尔> 是具有Value属性,这将是由具体的类来实现的接口。但具体的类可能需要一些细节(如什么是在app.config中键的名称)或不(我可能只是想返回总是如此)。

However, with this I have one main problem: How do I know how to create an instance of TEnableLogging (which is a IGetValue<bool>)? Oh, assume that IGetValue<bool> is an interface which has a Value property, which will be implemented by the concrete class. But the concrete class may need some specifics (like what's the name of the key in app.config) or not (I may simply want to return always true).

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

I'm relatively new to dependency injection, so maybe I'm thinking in a wrong way. Does anyone have any ideas on how to accomplish this?

(你可以回答使用另一个DI图书馆,我不会介意的。我想我只需要抢概念它。)

(You may answer using another DI library, I won't mind. I think I just need to grab the concept of it.)

推荐答案

您肯定是标题这里走错了路。

You are definitely heading the wrong way here.

几年前,我构建了包含的接口非常喜欢你的 IApplicationSettings 的应用程序。我相信,我把它命名为 IApplicationConfiguration ,但它包含了所有应用程序的配置值,以及

Some years ago I built an application that contained an interface much like your IApplicationSettings. I believe I named it IApplicationConfiguration, but it contained all application's configuration values as well.

虽然它帮助我做我的应用程序在第一次测试的,一段时间后开始设计的方式来获得。很多的实现取决于该接口上的,但它一直改变了很多,它的实施,以及测试版本。

Although it helped me make my application testable at first, after some time the design started to get in the way. A lot of implementations depended on that interface, but it kept changing a lot and with it the implementation, and the test version.

就像你,我实现了一些延迟加载,但是这有一个可怕的一面。当配置值之一是缺少,我才发现原来它没有当值被称为首次。这导致那是难以核实配置。

Just like you I implemented some lazy loading, but this had a terrible down side. When one of the configuration values was missing, I only found out that it did when the value was called for the first time. This resulted in a configuration that was hard to verify.

我花了一对夫妇重构什么问题的核心是迭代。大接口是一个问题。我的 IApplicationConfiguration 类是违反接口分离原则和结果可维护性差。

It took me a couple of iterations of refactoring what the core of the problem was. Big interfaces are a problem. My IApplicationConfiguration class was violating the Interface Segregation Principle and the result was poor maintainability.

在最后,我发现这个界面是完全无用的。除了违反ISP,这些配置值描述的实现细节,而不是使一个应用广泛的抽象概念,它可以直接与他们所需要的配置值提供每个执行好多了。

In the end I found out that this interface was completely useless. Besides violating the ISP, those configuration values described an implementation detail and instead of making an application wide abstraction, it was much better to supply each implementation directly with the configuration value they needed.

当你做到这一点,最容易做的事情是通过该配置值属性值。有了简单的喷油器,你可以使用 RegisterInitializer 方法如下:

When you do this, the easiest thing to do is to pass that configuration value as property value. With Simple Injector you can use the RegisterInitializer method for this:

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

container.RegisterInitializer<Logger>(logger =>
{
    logger.EnableLogging = enableLogging;
});

在此过程中, EnableLogging设置值读取配置文件只有一次,并在应用程序启动是这样做的。这使得它快速,使得它无法在当值是缺少应用程序启动。

When doing this, the enableLogging value is read just once from the configuration file and is done so during application startup. This makes it fast and makes it fail at application startup when the value is missing.

如果由于某种原因,你需要延迟读数(从数据库实例),你可以使用懒< T>

If for some reason you need to delay the reading (from database for instance), you can use Lazy<T>:

Lazy<bool> copyLocal = new Lazy<bool>(() =>
    container.GetInstance<IDatabaseManager>().RunQuery(CopyLocalQuery));

container.RegisterInitializer<FileCopier>(copier =>
{
    copier.CopyLocal = copyLocal.Value;
});



而不是通过使用属性的值,你也可以用构造函数的参数,但是这是一个有点困难实现。以一个看一些想法这篇文章

如果你发现自己注册为众多注册相同的配置价值,你可能缺少的抽象。看看这个:

If you find yourself registering the same configuration value for many registrations, you are probably missing an abstraction. Take a look at this:

container.RegisterInitializer<UserRepository>(rep => { 
    rep.ConnectionString = connectionString; });
container.RegisterInitializer<OrderRepository>(rep => { 
    rep.ConnectionString = connectionString; });
container.RegisterInitializer<CustomerRepository>(rep => { 
    rep.ConnectionString = connectionString; });
container.RegisterInitializer<DocumentRepository>(rep => { 
    rep.ConnectionString = connectionString; });

在这种情况下,你可能错过了一个 IDatabaseFactory IDatabaseManager 抽象,你应该做这样的事情:

In this case you are probably missing an IDatabaseFactory or IDatabaseManager abstraction, and you should do something like this:

container.RegisterSingle<IDatabaseFactory>(new SqlDatabaseFactory(connectionString));

这篇关于如何使用依赖注入,获得来自多个来源的配置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆