依赖注入和的AppSettings [英] Dependency Injection and AppSettings

查看:177
本文介绍了依赖注入和的AppSettings的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

比方说,我定义我的应用程序浏览器实现类:

Let's say I am defining a browser implementation class for my application:

class InternetExplorerBrowser : IBrowser {
    private readonly string executablePath = @"C:\Program Files\...\...\ie.exe";

    ...code that uses executablePath
}

这乍一看,看起来像一个不错的主意,因为 executablePath 数据是使用它的代码附近。

This might at first glance to look like a good idea, as the executablePath data is near the code that will use it.

问题是当我尝试我的其他计算机上运行同样的应用程序中,有一个外国语言操作系统: executablePath 将有一个不同的值

The problem comes when I try to run this same application on my other computer, that has a foreign-language OS: executablePath will have a different value.

我可以通过AppSettings的单例类解决这个(或其等价物之一),但是没有人知道我的课实际上是依赖于此的AppSettings类(这违背DI ideias)。这可能造成困难单元测试了。

I could solve this through an AppSettings singleton class (or one of its equivalents) but then no-one knows my class is actually dependent on this AppSettings class (which goes against DI ideias). It might pose a difficulty to Unit-Testing, too.

我可以有解决这两个问题 executablePath 传递通过构造函数:

I could solve both problems by having executablePath being passed in through the constructor:

class InternetExplorerBrowser : IBrowser {
    private readonly string executablePath;

    public InternetExplorerBrowser(string executablePath) {
        this.executablePath = executablePath;
    }
}



但这样会提高我的<$ C $问题C>构成根(将尽一切所需的类布线启动法),然后该方法已知道怎样接线的事情和已经知道所有这些小设置数据:

but this will raise problems in my Composition Root (the startup method that will do all the needed classes wiring) as then that method has to know both how to wire things up and has to know all these little settings data:

class CompositionRoot {
    public void Run() {
        ClassA classA = new ClassA();

        string ieSetting1 = "C:\asdapo\poka\poskdaposka.exe";
        string ieSetting2 = "IE_SETTING_ABC";
        string ieSetting3 = "lol.bmp";

        ClassB classB = new ClassB(ieSetting1);
        ClassC classC = new ClassC(B, ieSetting2, ieSetting3);

        ...
    }
}



这会变成很容易弄得一塌糊涂。

which will turn easily a big mess.

我可以代替传递表单

interface IAppSettings {
    object GetData(string name);
}

要需要某种设置的所有类。然后,我既可以实现这个无论是作为其中嵌入的所有设置普通班或读取数据了一个XML文件中的类,东西沿线。如果这样做,我应该对整个系统的一般AppSettings的类的实例,或者有与每个可能需要一个类的AppSettings的类?这当然看起来好像有点矫枉过正。此外,拥有所有的应用程序setings在同一个地方可以很容易地看看,看看什么可能是我需要tryign移动程序到不同的平台的时候做的所有更改。

to all the classes that need some sort of settings. Then I could either implement this either as a regular class with all the settings embedded in it or a class that reads data off a XML file, something along the lines. If doing this, should I have a general AppSettings class instance for the whole system, or have an AppSettings class associated to each class that might need one? That certainly seems like a bit of an overkill. Also, have all the application setings in the same place makes it easy to look and see what might be all the changes I need to do when tryign to move the program to different platforms.

什么可能是解决这个常见的情况。

What might be the best way to approach this common situation?

和什么有关使用 IAppSettings 及其所有设置的吗?

And what about using an IAppSettings with all its settings hardcoded in it?

interface IAppSettings {
    string IE_ExecutablePath { get; }
    int IE_Version { get; }
    ...
}

这将允许编译时类型-安全。如果我看到了界面/混凝土类增长太多,我可以创建表格 IMyClassXAppSettings 的其他较小的接口。难道是一个负担过重的MED /大规模的项目来承担?

This would allow for compile-time type-safety. If I saw the interface/concrete classes grow too much I could create other smaller interfaces of the form IMyClassXAppSettings. Would it be a burden too heavy to bear in med/big sized projects?

我也阅读有关AOP,其优点处理跨部门,关注(我想这就是其中之一)。难道不是也提供了解决问题的对策?也许标注变量是这样的:

I've also reading about AOP and its advantages dealing with cross-cutting-concerns (I guess this is one). Couldn't it also offer solutions to this problem? Maybe tagging variables like this:

class InternetExplorerBrowser : IBrowser {
    [AppSetting] string executablePath;
    [AppSetting] int ieVersion;

    ...code that uses executablePath
}

然后,编译项目的时候,我们也不得不编译时的安全性(有编译器检查,我们实际执行的代码,将编织数据。这会,当然,束缚我们的API来这方面的问题。

Then, when compiling the project we'd also have compile time safety (having the compiler check that we actually implemented code that would weave in data. This would, of course, tie our API to this particular Aspect.

推荐答案

个人类应该从基础设施尽可能自由 - 构造,如 IAppSettings IMyClassXAppSettings [AppSetting] 流血构成的详细上课其中,在其最简单的,真的只能靠原始值,如 executablePath 依赖注入的艺术是在关注保理。

The individual classes should be as free from infrastructure as possible - constructs like IAppSettings, IMyClassXAppSettings, and [AppSetting] bleed composition details to classes which, at their simplest, really only depend on raw values such as executablePath. The art of Dependency Injection is in the factoring of concerns.

我已经实现了使用的 Autofac ,它也有类似的模块Ninject并应导致类似的代码(我意识到这个问题并没有提到Ninject,但OP确实在注释)。

I have implemented this exact pattern using Autofac, which has modules similar to Ninject and should result in similar code (I realize the question doesn't mention Ninject, but the OP does in a comment).

模块通过子系统组织应用程序。模块暴露一个子系统的配置元素:

Modules organize applications by subsystem. A module exposes a subsystem's configurable elements:

public class BrowserModule : Module
{
    private readonly string _executablePath;

    public BrowserModule(string executablePath)
    {
        _executablePath = executablePath;
    }

    public override void Load(ContainerBuilder builder)
    {
        builder
            .Register(c => new InternetExplorerBrowser(_executablePath))
            .As<IBrowser>()
            .InstancePerDependency();
    }
}

这留下同样的问题组成根源:它必须提供 executablePath 的价值。为了避免配置汤,我们可以写一个自包含的模块,读取配置设置,并将其传递给 BrowserModule

This leaves the composition root with the same problem: it must supply the value of executablePath. To avoid the configuration soup, we can write a self-contained module which reads configuration settings and passes them to BrowserModule:

public class ConfiguredBrowserModule : Module
{
    public override void Load(ContainerBuilder builder)
    {
        var executablePath = ConfigurationManager.AppSettings["ExecutablePath"];

        builder.RegisterModule(new BrowserModule(executablePath));
    }
}

您可以考虑使用自定义配置节,而不是的AppSettings ;变化将本地化的模块:

You could consider using a custom configuration section instead of AppSettings; the changes would be localized to the module:

public class BrowserSection : ConfigurationSection
{
    [ConfigurationProperty("executablePath")]
    public string ExecutablePath
    {
        get { return (string) this["executablePath"]; }
        set { this["executablePath"] = value; }
    }
}

public class ConfiguredBrowserModule : Module
{
    public override void Load(ContainerBuilder builder)
    {
        var section = (BrowserSection) ConfigurationManager.GetSection("myApp.browser");

        if(section == null)
        {
            section = new BrowserSection();
        }

        builder.RegisterModule(new BrowserModule(section.ExecutablePath));
    }
}

这是一个很好的模式,因为每个子系统都有一个独立配置,它被在一个地方阅读。这里唯一的好处是更明显的意图。对于非 - 字符串值或复杂的模式,但是,我们可以让 System.Configuration 做繁重

This is a nice pattern because each subsystem has an independent configuration which gets read in a single place. The only benefit here is a more obvious intent. For non-string values or complex schemas, though, we can let System.Configuration do the heavy lifting.

这篇关于依赖注入和的AppSettings的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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