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

cyej8jka  于 2023-05-08  发布在  .NET
关注(0)|答案(5)|浏览(180)

我的问题是关于使用标准的.NET配置对象和自定义配置元素(可以通过扩展System.Configuration.ConfigurationSection类来定义)。我们通常从System.Configuration.ConfigurationManager类的方法中获得这些,GetSection(...)就是一个例子。
加载的配置对象似乎是一个 merged 配置对象,包含应用程序配置文件(开发人员可能创建的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中都定义了configuration部分,那么当我调用ConfigurationManager.GetSection("MyCustomSection")时,我需要获得两个配置的合并版本。理想情况下,如果我能够执行this MSDN article中描述的合并或使用Wayback machine link,那就太好了
通常,我会编写自己的配置管理器类,并尽可能地获得所需的结果,但假设.NET框架可以为machine.configapp.config工作,我想我可能会受益于框架的内置功能。此外,如果我确实应该求助于自己的配置管理器实现,我不知道如何手动触发这样的合并。
那么,是否可以利用内置的配置节/元素与自定义配置文件合并的机制呢?我特别感兴趣的是开发和支持自定义配置部分。System.Configuration命名空间包含构建配置节和元素的基本对象,这些对象允许一些与合并相关的设置(例如设置适当的ConfigurationElementCollectionType)。这些是仅与machine.config(或Web应用中的多层web.config文件)合并有关,还是可以手动触发预加载配置文件的合并?我试图避免必须在任何自定义配置对象中支持自定义合并,并且可能忘记支持System.Configuration.xml中的现有设置。

更新

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

**注:**我希望有一个更好的解决方案,将工作在.NET 3.0+。如果您的答案针对更高版本的框架,请添加注解。

ttvkxqim

ttvkxqim1#

.NET配置系统不是超级灵活的
这条评论抓住了它,并解释了为什么你已经找了很长一段时间,还没有找到任何东西。并非所有的. NETFramework部分都是“好”的,System.Configuration应该位于最底部。这是荒谬的过度设计的东西,最终是一个简单的任务,但在同一时间变成了一些非常不灵活。很难逆向工程这是如何发生的,我认为它是由于安全问题而瘫痪的。也许可以理解,征用一个程序的数据总是一个相当大的风险。
我所知道的唯一扩展点是编写自己的SettingsProvider。该框架只有一个用于一般用途的LocalFileSettingProvider类。而且非常不灵活,没有任何方法可以改变它的行为。对于自定义设置提供程序,有一个不错的例子,RegistrySettingsProvider sample演示了一个在注册表中存储设置的提供程序。这可能是一个很好的开始写你自己的。
也许这并不是您所想的,请不要考虑您可以在System.Configuration中进行分层的想法。

xtfmy6hx

xtfmy6hx2#

看看下面的代码,知道要加载配置或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);

}
camsedfj

camsedfj3#

正如silver所指出的,配置良好的ExeConfigurationFileMap可以完成这项工作,但需要付出一定的代价。
我以他为例,做了一个工作版本。
以下是我为测试目的合并的两个配置文件:

自定义配置

<?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>

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>

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

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上测试)
  • 不需要反射或其他黑魔法的东西来触发行为。

cons

  • 不太确定,但是通过设置map.MachineConfigFilename = PathToCustomConfig;,我假设我删除了由真实的的machine.config文件设置的任何值。这很容易出错,对于web应用程序应该避免,因为它们中的大多数依赖于真实的的machine.config中的内容。
  • 需要传递应用程序配置文件的位置,因为它不再是自动确定的。因此,需要弄清楚在编译代码时app.config将如何命名(通常是AssemblyName.exe.config)
  • 你可以通过这种方式合并 two only 文件的内容。如果一个人需要一个更大的层次结构,那么这将无法很好地工作。

我仍在完善的技术过程中,因此我会回来更新这篇文章一旦完成。

wbgh16ku

wbgh16ku4#

默认情况下,配置继承实际上有3个级别:Machine、Exe和User(可以是Roaming或Local)。如果您自己加载配置文件,您可以将ExeConfigurationFileMap类与ConfigurationManager.OpenMappedExeConfiguration结合使用,以加载您自己的自定义配置层次结构。
我不认为你可以用这个来改变ConfigurationManager类的默认路径,但是你会得到一个Configuration元素,它可以用来从加载的配置层次结构中获取任何部分。
如果您查看How to read configSections的答案,其中包括一些关于确定在层次结构中声明了哪些级别的节的注解(使用SectionInformation)

var localSections = cfg.Sections.Cast<ConfigurationSection>()
       .Where(s => s.SectionInformation.IsDeclared);
yyhrrdl8

yyhrrdl85#

我们可能需要使用类ConfigurationSection和ConfigurationProperty
有关https://blog.danskingdom.com/adding-and-accessing-custom-sections-in-your-c-app-config/实现的详细信息,请参阅下面的链接

相关问题