如何使用Settings(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)
{
}
}
}
}
答案 0 :(得分:27)
配置类减少了消费者的凝聚力并增加了消费者的耦合。这是因为可能有许多设置与您的类所需的一个或两个无关,但为了实现依赖性,IConfiguration
的实现必须为所有访问器提供值,甚至是不相关的。
它还将您的课程与基础知识相结合:“将这些值配置在一起”等详细信息从应用程序配置中渗透到您的类中,从而增加了受不相关系统更改影响的表面区域。
共享配置值的最简单,最灵活的方法是使用构造函数注入值本身,外部化基础结构问题。但是,在对另一个答案的评论中,您表明您害怕拥有大量构造函数参数,这是一个有效的问题。
要认识到的关键点是原始和复杂依赖关系之间没有区别。无论您是依赖于整数还是接口,它们都是您不知道且必须被告知的东西。从这个角度来看,IConfiguration
与IDependencies
一样有意义。大型构造函数表明,无论参数是原始的还是复杂的,类都有太多的责任。
考虑将int
,string
和bool
视为与任何其他依赖关系一样。它将使您的课程更清晰,更专注,更耐变,更容易进行单元测试。
答案 1 :(得分:24)
要实现的重要部分是配置只是驱动应用程序行为的几个值源之一。
第二个选项(非静态配置)最好,因为它使您能够完全将消费者与配置值的来源分离。但是,界面不是必需的,因为配置设置通常最好建模为值对象。
如果您仍想从配置文件中读取值,可以从应用程序的Composition Root执行此操作。使用StructureMap,它可能看起来像这样:
var config = (MyConfigurationSection)ConfigurationManager.GetSection("myConfig");
container.Configure(r => r
.For<Consumer>()
.Ctor<MyConfigurationSection>()
.Is(config));
答案 2 :(得分:2)
一种方法是注入像你发布的配置界面。以下是其他几种方式。
公开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();
答案 3 :(得分:0)
在我的应用程序中,我使用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
文件中提取了大部分内容,并且还有一些硬编码值。
我的例子不是逻辑流程控制(就像你的例子一样),但如果是这样的话就会有相同的效果。
执行此操作的另一种方法是执行服务定位器类型模式,在此模式中,您要求Dependency Injection容器即时获取配置类的实例。服务地点is considered an anti-pattern generally,但可能仍然对您有用。