使用依赖注入将配置代码排除在逻辑代码之外的方法

发布于 2024-12-02 20:49:10 字数 1943 浏览 4 评论 0原文

如何使用设置(ApplicationSettingsBase)和依赖注入将所有配置文件代码保留在我的逻辑代码之外?

我所说的配置是指客户特定的配置文件。

我真的必须在每次需要时注入配置类还是还有其他模式?

如果能得到一些示例代码那就太好了!

示例:

静态配置:

public static class StaticConfiguration
{
    public static bool ShouldApplySpecialLogic { get; set; }
    public static string SupportedFileMask { get; set; }
}

public class ConsumerOfStaticConfiguration
{
    public void Process()
    {
        if (StaticConfiguration.ShouldApplySpecialLogic)
        {
            var strings = StaticConfiguration.SupportedFileMask.Split(',');
            foreach (var @string in strings)
            {

            }
        }
    }
}

非静态配置:

public interface IConfiguration
{
    bool ShouldApplySpecialLogic { get; set; }
    string SupportedFileMask { get; set; }
}

public class Configuration : IConfiguration
{
    public bool ShouldApplySpecialLogic { get; set; }
    public string SupportedFileMask { get; set; }
}

public class Consumer
{
    private readonly IConfiguration _configuration;

    public Consumer(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public void Process()
    {
        if (_configuration.ShouldApplySpecialLogic)
        {
            var strings = _configuration.SupportedFileMask.Split(',');
            foreach (var @string in strings)
            {

            }
        }
    }
}

具有非静态配置的静态上下文:

public static class Context
{
    public static IConfiguration Configuration { get; set; }
}

public class ConsumerOfStaticContext
{
    public void Process()
    {
        if (Context.Configuration.ShouldApplySpecialLogic)
        {
            var strings = Context.Configuration.SupportedFileMask.Split(',');
            foreach (var @string in strings)
            {

            }
        }
    }
}

How can keep all the configuration file code out of my logic code using Settings (ApplicationSettingsBase) and Dependency Injection?

With configuration I mean a customer specific configuration file.

Do I really have to inject a configuration class everytime I need it or is there another pattern?

It would be great to get some sample code!

Samples:

Static Configuration:

public static class StaticConfiguration
{
    public static bool ShouldApplySpecialLogic { get; set; }
    public static string SupportedFileMask { get; set; }
}

public class ConsumerOfStaticConfiguration
{
    public void Process()
    {
        if (StaticConfiguration.ShouldApplySpecialLogic)
        {
            var strings = StaticConfiguration.SupportedFileMask.Split(',');
            foreach (var @string in strings)
            {

            }
        }
    }
}

Non static Configuration:

public interface IConfiguration
{
    bool ShouldApplySpecialLogic { get; set; }
    string SupportedFileMask { get; set; }
}

public class Configuration : IConfiguration
{
    public bool ShouldApplySpecialLogic { get; set; }
    public string SupportedFileMask { get; set; }
}

public class Consumer
{
    private readonly IConfiguration _configuration;

    public Consumer(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public void Process()
    {
        if (_configuration.ShouldApplySpecialLogic)
        {
            var strings = _configuration.SupportedFileMask.Split(',');
            foreach (var @string in strings)
            {

            }
        }
    }
}

Static Context with non static configuration:

public static class Context
{
    public static IConfiguration Configuration { get; set; }
}

public class ConsumerOfStaticContext
{
    public void Process()
    {
        if (Context.Configuration.ShouldApplySpecialLogic)
        {
            var strings = Context.Configuration.SupportedFileMask.Split(',');
            foreach (var @string in strings)
            {

            }
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

白鸥掠海 2024-12-09 20:49:10

配置类降低了消费者的内聚性并增加了耦合性。这是因为可能有许多设置与您的类所需的一两个设置无关,但为了满足依赖性,您的 IConfiguration 实现必须为所有访问器提供值,甚至是不相关的。

它还将您的类与基础设施知识结合起来:诸如“这些值配置在一起”之类的详细信息从应用程序配置中渗透到您的类中,从而增加了受不相关系统更改影响的表面积。

共享配置值的最简单、最灵活的方法是使用值本身的构造函数注入,从而外部化基础设施问题。然而,在对另一个答案的评论中,您表示您害怕拥有大量构造函数参数,这是一个合理的担忧。

需要认识到的关键点是原始依赖关系和复杂依赖关系之间没有区别。无论您依赖于整数还是接口,它们都是您不知道但必须被告知的事情。从这个角度来看,IConfigurationIDependency 一样有意义。大型构造函数表明一个类有太多的责任,无论参数是原始的还是复杂的。

考虑像对待任何其他依赖项一样对待 intstringbool。它将使你的类更清晰、更集中、更能抵抗变化,并且更容易进行单元测试。

Configuration classes reduce cohension and increase coupling in the consumers. This is because there may be many settings that don't relate to the one or two needed by your class, yet in order to fulfill the dependency, your implementation of IConfiguration must supply values for all of the accessors, even the irrelevant ones.

It also couples your class to infrastructure knowledge: details like "these values are configured together" bleed out of the application configuration and into your classes, increasing the surface area affected by changes to unrelated systems.

The least complex, most flexible way to share configuration values is to use constructor injection of the values themselves, externalizing infrastructure concerns. However, in a comment on another answer, you indicate that you are scared of having a lot of constructor parameters, which is a valid concern.

The key point to recognize is that there is no difference between primitive and complex dependencies. Whether you depend on an integer or an interface, they are both things you don't know and must be told. From this perspective, IConfiguration makes as much sense as IDependencies. Large constructors indicate a class has too much responsibility regardless of whether the parameters are primitive or complex.

Consider treating int, string and bool like you would any other dependency. It will make your classes cleaner, more focused, more resistant to change, and easier to unit test.

过度放纵 2024-12-09 20:49:10

需要认识到的重要一点是,配置只是驱动应用程序行为的多个值源之一。

第二个选项(非静态配置)是最好的,因为它使您能够将使用者与配置值的来源完全解耦。但是,该接口不是必需的,因为配置设置通常最好建模为值对象

如果您仍想从配置文件中读取值,可以从应用程序的 组合根。使用 StructureMap,它可能看起来像这样:

var config = (MyConfigurationSection)ConfigurationManager.GetSection("myConfig");

container.Configure(r => r
    .For<Consumer>()
    .Ctor<MyConfigurationSection>()
    .Is(config));

The important part to realize is that configuration is only one among several sources of values that drive your application's behavior.

The second option (non-static configuration) is best because it enables you to completely decouple the consumer from the source of the configuration values. However, the interface isn't required, as configuration settings are normally best modeled as Value Objects.

If you still want to read the values from a configuration file, you can do that from the application's Composition Root. With StructureMap, it might looks something like this:

var config = (MyConfigurationSection)ConfigurationManager.GetSection("myConfig");

container.Configure(r => r
    .For<Consumer>()
    .Ctor<MyConfigurationSection>()
    .Is(config));
丢了幸福的猪 2024-12-09 20:49:10

一种方法是像您发布的那样注入配置接口。这里还有其他一些方法。

公开 Setter

class Consumer
{
    public bool ShouldApplySpecialLogic { get; set; }

    ...
}

在组合根中,您可以读取配置文件或对其进行硬编码。 Autofac 示例:

builder.RegisterType<Consumer>().AsSelf()
    .OnActivated(e => e.Instance.ShouldApplySpecialLogic = true);

时,这可能才是可取的:

有一个良好的默认构造函数注入

public class Server
{
    public Server(int portToListenOn) { ... }
}

只有当您在组合根中

builder.Register(c => new Server(12345)).AsSelf();

One way is to inject a configuration interface like you post. Here are a couple other ways.

Exposing a Setter

class Consumer
{
    public bool ShouldApplySpecialLogic { get; set; }

    ...
}

In the composition root, you can read a config file or hardcode it. Autofac example:

builder.RegisterType<Consumer>().AsSelf()
    .OnActivated(e => e.Instance.ShouldApplySpecialLogic = true);

This is probably only advisable when you have a good default

Constructor Injection

public class Server
{
    public Server(int portToListenOn) { ... }
}

In the composition root:

builder.Register(c => new Server(12345)).AsSelf();
疧_╮線 2024-12-09 20:49:10

在我的应用程序中,我使用 IoC 执行了您上面所做的操作。也就是说,让我的 IoC 容器(也包括 StructureMap)将 IApplicationSettings 注入到我的类中。

例如,在 ASP.NET MVC3 项目中,它可能如下所示:

Public Class MyController
    Inherits Controller

    ...
    Private ReadOnly mApplicationSettings As IApplicationSettings

    Public Sub New(..., applicationSettings As IApplicationSettings)
        ...
        Me.mApplicationSettings = applicationSettings
    End Sub

    Public Function SomeAction(custId As Guid) As ActionResult
         ...

         ' Look up setting for custId
         ' If not found fall back on default like
         viewModel.SomeProperty = Me.mApplicationSettings.SomeDefaultValue

         Return View("...", viewModel)
    End Function
End Class

我的 IApplicationSettings 实现从应用程序的 .config 文件中提取大部分内容,并具有一些硬编码值也在那里。

我的示例不是逻辑流控制(就像您的示例一样),但如果是的话,它的工作原理是一样的。

另一种方法是使用服务定位器类型模式,在这种模式中,您要求依赖注入容器即时获取配置类的实例。 Service-Location 通常被视为反模式,但仍可能是对你有用。

In my applications I do what you have done above with IoC. That is to say, having my IoC container (StructureMap also) inject an IApplicationSettings into my classes.

For example, in an ASP.NET MVC3 project it may look like:

Public Class MyController
    Inherits Controller

    ...
    Private ReadOnly mApplicationSettings As IApplicationSettings

    Public Sub New(..., applicationSettings As IApplicationSettings)
        ...
        Me.mApplicationSettings = applicationSettings
    End Sub

    Public Function SomeAction(custId As Guid) As ActionResult
         ...

         ' Look up setting for custId
         ' If not found fall back on default like
         viewModel.SomeProperty = Me.mApplicationSettings.SomeDefaultValue

         Return View("...", viewModel)
    End Function
End Class

My implementation of IApplicationSettings pulls most things from the app's .config file and has a few hard-coded values in there as well.

My example wasn't logic flow-control (like your example), but it would have worked just the same if it was.

The other way to do this would be to do a service-locator type pattern, where you ask your Dependency Injection container to get you an instance of the configuration class on-the-fly. Service-Location is considered an anti-pattern generally, but might still be of use to you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文