Merging custom configuration sections at runtime in .NET
我的问题是如何使用标准.NET配置对象和自定义配置元素(可以通过扩展
加载的配置对象似乎是一个合并的配置对象,其中包含应用程序配置文件(开发人员可能创建的app.config或web.config文件)中存在的设置以及machine.config文件中定义的内容(后者随.NET Framework安装一起提供)。
因此,我们可以假定配置首先是以分层方式加载machine.config,并且任何用户定义的配置覆盖了默认设置,可以这样看:
- 机器配置
- app.config(覆盖/合并machine.config中的各个元素)
我的目标是创建多个配置层,以便在machine.config和app.config文件之间(如果可能,在之后)可能存在其他配置文件:
- 机器配置
- custom.config(自定义配置干扰machine.config和app.config文件)
- app.config-现在app.config与machine.config和custom.config合并。
- custom.config(自定义配置干扰machine.config和app.config文件)
更新
要点是-如果我在custom.config和app.config中都定义了配置部分,那么当我调用
通常,我会编写自己的Configuration Manager类,并尽我所能获得所需的结果,但是假设.NET框架在machine.config和app.config中具有此功能,我认为我可能会从框架的内置功能中获益。此外,如果我真的应该求助于自己的配置管理器实现,那么我不知道如何手动触发这种合并。
那么,是否可以利用配置节/元素与自定义配置文件合并的内置机制?我对为自定义配置部分开发和支持这个特别感兴趣。
更新
为了回应现有的答案和评论,我想做一个重要的澄清。我可以从当前的应用程序设置(app.config/web.config)和自定义的物理文件加载
注意:我希望有更好的解决方案可以在.NET 3.0+上工作。如果您的答案针对的是更高版本的框架,请添加注释。
0
这条评论将其钉住,并解释了为什么你一直在寻找很长时间,还没有找到任何东西。并非所有的.NET框架部分都是"好的",系统配置应该放在最底层。它被荒谬地过度设计为最终是一个简单的任务,但同时变成了一个极其不灵活的东西。很难对这件事进行逆向工程,我认为它因为安全问题而瘫痪了。也许可以理解,霸占一个程序的数据总是一个相当大的风险。
我知道的唯一扩展点是编写自己的设置提供者。框架只有一个用于常规用途,即localfilesettingprovider类。也非常不灵活,没有任何方法可以改变它的行为。有一个很好的示例可用于自定义设置提供程序,RegistrySettingsProvider示例演示在注册表中存储设置的提供程序。这可能是你自己写作的一个好起点。
也许并不是你所想的那样,请记住你可以进入System.Configuration内部的分层。
正如Silver所指出的,配置良好的
我以他的例子为例,做了一个有效的版本。
以下是我为测试目的合并的两个配置文件:
Cuff.CONFIG
2App.CONFIG
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="custom" type="..."/> </configSections> <custom> <singleProperty id="main" value="OverriddenValue" /> <propertyCollection> <property id="1" value="OverridenOne" /> <property id="2" value="Two" /> <property id="3" value="Three" /> </propertyCollection> </custom> </configuration> |
我使用以下代码测试合并的设置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | var map = new ExeConfigurationFileMap(); map.MachineConfigFilename = PathToCustomConfig; map.ExeConfigFilename = PathToAppConfig; var configuration = ConfigurationManager.OpenMappedExeConfiguration( map, ConfigurationUserLevel.None); var section = configuration.GetSection("custom") as CustomConfigSection; Assert.IsNotNull(section); Assert.AreEqual(section.SingleProperty.Value,"OverriddenValue"); Assert.AreEqual(section.PropertyCollection.Count, 4); // Needed to map the properties as dictionary, not to rely on the property order var values = section.PropertyCollection .Cast<SimpleConfigElement>() .ToDictionary(x => x.ID, x => x.Value); Assert.AreEqual(values["1"],"OverridenOne"); Assert.AreEqual(values["2"],"Two"); Assert.AreEqual(values["3"],"Three"); Assert.AreEqual(values["4"],"Four"); |
这种方法的优点
- 我让内置的合并逻辑工作
- 适用于旧版本的.NET(在3.5上测试)
- 不需要反射或其他黑色魔法来触发行为。
欺骗
- 不太确定,但是通过设置
map.MachineConfigFilename = PathToCustomConfig; ,我假设删除了实际machine.config 文件设置的任何值。这很容易出错,对于Web应用程序应该避免,因为大多数应用程序都依赖于真实的machine.config 中的内容。 - 需要传递应用程序配置文件的位置,因为它不再是自动确定的。因此,需要弄清楚在编译代码时如何命名
app.config (通常是assemblyname.exe.config) - 您可以这样合并两个文件的内容。如果一个人需要一个更大的层次结构,那么这就不能很好地工作。
我仍在完善这项技术,因此我会在完成后返回更新这篇文章。
查看以下代码,了解如何加载配置或exe配置。一旦清除了以下代码,您就可以根据需要自定义加载和合并(加载两次,并用另一个重写)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | private static void InitConfiguration() { var map = new ExeConfigurationFileMap(); var AssemblyConfigFile =""; if (File.Exists(AssemblyConfigFile)) map.ExeConfigFilename = AssemblyConfigFile; else map.ExeConfigFilename = Path.Combine(Environment.CurrentDirectory, Environment.GetCommandLineArgs()[0]+".config"); var Configuration = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None); var serviceModelSectionGroup = ServiceModelSectionGroup.GetSectionGroup(Configuration); } |
默认情况下,配置继承实际上有三个级别:machine、exe和user(可以是漫游或本地)。如果您自己加载配置文件,则可以将ExeConfigurationFileMap类与ConfigurationManager.OpenMappedExeConfiguration结合使用来加载您自己的自定义配置层次结构。
我不认为您可以用这个更改configurationManager类的默认路径,但是您将得到一个配置元素,它可以用来从加载的配置层次结构中获取任何部分。
如果您查看了如何读取配置节的答案,它将包含一些关于确定在层次结构中声明了哪些级别的节的注释(使用节信息)。
1 2 | var localSections = cfg.Sections.Cast<ConfigurationSection>() .Where(s => s.SectionInformation.IsDeclared); |