在.NET中运行时合并自定义配置节

Iva*_*vov 17 .net c# configuration configurationmanager

我的问题是关于使用标准.NET配置对象和自定义配置元素(可以通过扩展System.Configuration.ConfigurationSection类来定义).我们通常从System.Configuration.ConfigurationManager类的方法中获得这些,GetSection(...)作为一个例子.

加载的配置对象似乎是一个合并的配置对象,其中包含应用程序配置文件(开发人员可能创建的app.configweb.config文件)中存在的设置以及machine.config文件中定义的内容(后者随.NET Framework安装而来).

因此,我们可以假设首先使用machine.config以分层方式加载配置,并且任何用户定义的配置都覆盖默认设置,并且可以像这样查看:

  • machine.config中
    • app.config(覆盖/合并在machine.config中找到的相应元素)

我的目标是创建多层配置,以便在machine.configapp.config文件之间(如果可能,之后)之间可能存在其他配置文件:

  • machine.config中
    • custom.config(自动配置干扰machine.config和app.config文件)
      • app.config - 现在app.configmachine.configcustom.config合并

更新

关键是 - 如果我在custom.configapp.config中定义了配置部分,我需要在调用时获得两个配置的合并版本ConfigurationManager.GetSection("MyCustomSection").理想情况下,如果我能够按照本MSDN文章中的描述执行合并,那将是很棒的.


通常,我会编写自己的配置管理器类,并尝试尽可能地获得所需的结果,但假设.NET框架对machine.configapp.config有效,我想我可能会受益于框架的内置功能.另外,我不知道如何手动触发这样的合并,如果我确实应该求助于我自己的配置管理器实现.

那么,是否可以利用配置部分/元素与自定义配置文件合并的内置机制?我特别感兴趣的是为自定义配置部分开发和支持它.该System.Configuration命名空间包含基础对象建立配置部分和元件,以及那些允许有关合并一些设置(如设置适当的ConfigurationElementCollectionType例如).这些是仅与machine.config(或Web应用程序中的多层web.config文件)合并,还是可以手动触发预加载配置文件的合并?我试图避免在任何自定义配置对象中支持自定义合并,并且可能忘记支持System.Configuration中的现有设置...


更新

根据现有的答案和评论,我想做一个重要的澄清.我能够ConfigurationSection从当前的应用程序设置(app.config/web.config)和我的custom.config物理文件加载对象.我需要知道是否有机会合并这些对象,而不需要通过框架中的一些内置方法进行反射和逐个属性比较.


注意:我希望能够更好地使用适用于.NET 3.0+的解决方案.如果您的答案针对更高版本的框架,请添加注释.

Han*_*ant 8

.NET配置系统不够灵活

这个评论指出了它,并解释了为什么你一直在寻找很长一段时间但还没有找到任何东西.并非所有的.NET Framework部件都是"好"的,System.Configuration值得在最底层找到它.对于那些最终是一项简单任务但同时又变得非常不灵活的东西来说,这是荒谬的过度设计.很难对这种情况进行逆向工程,我认为它因安全问题而陷入瘫痪.或许可以理解的是,用数据来征用一个程序总是存在相当大的风险.

我所知道的唯一扩展点是编写自己的SettingsProvider.该框架只有一个用于一般用途的LocalFileSettingProvider类.也非常不灵活,没有任何方法可以改变它的行为.自定义设置提供程序有一个不错的示例,RegistrySettingsProvider示例演示了一个在注册表中存储设置的提供程序.它可以是编写自己的好起点.

也许并不完全是你的想法,请抓住你可以进入System.Configuration内部分层的想法.

  • Hmya,你做得非常迅速,完全没有任何意义可以使用它.自己搜索特定的.config文件肯定是你真正想做的事情. (2认同)

sil*_*ver 5

看一下下面的代码,它知道加载您的配置或 exe 配置。一旦您清楚了以下代码,您就可以根据需要自定义加载和合并(加载两次并用另一个覆盖一个)。

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);

}
Run Code Online (Sandbox Code Playgroud)


Iva*_*vov 5

正如白银指出的那样,配置良好的设备ExeConfigurationFileMap可以以一定的成本完成这项工作。

我以他为榜样,并做了一个可行的版本。

这是我出于测试目的而合并的两个配置文件:

custom.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="custom" type="..." />
  </configSections>
  <custom>
    <singleProperty id="main" value="BaseValue" />
    <propertyCollection>
      <property id="1" value="One" />
      <property id="4" value="Four" />
    </propertyCollection>
  </custom>
</configuration>
Run Code Online (Sandbox Code Playgroud)

app.config

<?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>
Run Code Online (Sandbox Code Playgroud)

我使用以下代码来测试合并的设置:

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");
Run Code Online (Sandbox Code Playgroud)

这种方法的优点

  • 我得到了内置的合并逻辑
  • 适用于旧版本的.NET(在3.5上测试)
  • 无需反射或其他黑魔法东西即可触发该行为。

缺点

  • 不太确定,但是通过设置,map.MachineConfigFilename = PathToCustomConfig;我假设我要删除实际machine.config文件设置的所有值。这可能容易出错,并且对于Web应用程序应避免使用,因为它们大多数依赖于真实情况。machine.config
  • 需要传递应用程序配置文件的位置,因为它不再自动确定。因此,需要弄清楚在app.config编译代码时(通常是AssemblyName.exe.config)如何命名。
  • 您可以通过这种方式合并两个仅文件的内容。如果需要更大的层次结构,那么这将无法正常工作。

我仍在完善技术,因此一旦完成,我将返回更新此帖子。