سؤال

I'm trying to make a copy of a ConfigurationSection from an external app.config file's configuration with the end-goal of merging it into the executing application's currently-loaded configuration.

The purpose is to allow numerous libraries, loaded into individual AppDomains, to receive the configuration from the executing application and have their own app.config settings merged so that reading any setting is a simple call to ConfigurationManager.AppSettings or ConfigurationManager.GetSection().

The issue I'm running into is making a clone of a ConfigurationSection.

What I've tried:

  1. ConfigurationSectionCloner in the Enterprise Library.

    Configuration externalConfig = ConfigurationManager.OpenExeConfiguration(externalLibPath);
    var section = externalConfig.GetSection("some.section");
    ConfigurationSectionCloner sectionCloner = new ConfigurationSectionCloner();
    section = sectionCloner.Clone(section);
    
    Configuration localConfig = ConfigurationManager.OpenExecConfiguration(ConfigurationUserLevel.None);
    localConfig.Sections.Add("some.section", section);
    
    • This runs fine, however, both localConfig.GetSection("some.section") and ConfigurationManager.GetSection("some.section") are empty.
    • Even calling localConfig.Save() (with any combination of parameters) doesn't populate the section.

  2. CompositeConfigurationSourceHandler in the Enterprise Library

    SystemConfigurationSource localConfig = new SystemConfigurationSource();
    FileConfigurationSource externalConfig = new FileConfigurationSource(externalLibPath + ".config");
    CompositeConfigurationSourceHandler ccsh = new CompositeConfigurationSourceHandler(externalConfig);
    ConfigurationSection section = externalConfig.GetSection("some.section");
    if (!ccsh.CheckAddSection("some.section", section)) {
        try {
            localConfig.Add("some.section", section);
        } catch (Exception) { }
    }
    
    • This throws an exception on the localConfig.add() line stating Cannot add a ConfigurationSection that already belongs to the Configuration.. The problem is that localConfig does not have that section. Even adding localConfig.Remove("some.section"); doesn't resolve.
    • I've also tried a number of combinations of *ConfigurationSource objects to see if they make a difference but none have.

Copying actual ApplicationSettings from the appSettings block, or even ConnectionStrings from the connectionStrings block is super-simple with a call such as ConfigurationManager.AppSettings.Set("some key", "some value"); but it doesn't seem as easy with ConfigurationSections.

Is there a way to copy, clone and/or just plain merge a ConfigurationSection from one configuration into another?

Notes:

  1. I don't want to merge physical files. Everything should happen during runtime and remain in-memory only.
  2. I don't want to write a custom class to represent each ConfigurationSection. The sections will be generic and unknown to the executing application.
هل كانت مفيدة؟

المحلول

I have figured out a method to merge a ConfigurationSection that has been read from any app.config file with the main executing application's loaded config!

The method extends my code for #1 using the ConfigurationSectionCloner from the Enterprise Library:

// open the external configuration
Configuration externalConfig = ConfigurationManager.OpenExeConfiguration(externalLibPath);

// get the section we're looking to clone & merge
var sectionToClone = externalConfig.GetSection(sectionName);

// "clone" the section; this will create a `DefaultSection` object - not a "pure clone" =[
ConfigurationSectionCloner sectionCloner = new ConfigurationSectionCloner();
var clonedSection = sectionCloner.Clone(sectionToClone);

// set the `Type` of the new section to the one we're cloning (most likely a NameValueCollection)
clonedSection.SectionInformation.Type = sectionToClone.SectionInformation.Type;

// set the new section's XML to match the original one (really? the cloner doesn't do this?!)
clonedSection.SectionInformation.SetRawXml(sectionToClone.SectionInformation.GetRawXml());

// open our local configuration (the "executing application's config)
Configuration localConfig = ConfigurationManager.OpenExecConfiguration(ConfigurationUserLevel.None);

// add the cloned section to it
localConfig.Sections.Add(sectionName, clonedSection);

// save the section (this can be any variation-of-parameters for `.Save()`)
localConfig.Save(ConfigurationSaveMode.Minimal);

// force ConfigurationManager to refresh the new section
ConfigurationManager.RefreshSection(sectionName);

Oddly, in all of my tests, the above steps are all required. The primary ones needed, per-section, are to set the new section's Type, to call SetRawXml(), and to refresh the section.

The downside to this approach, against what my original desires were, is that the call to localConfig.Save() saves it to disk overwriting the original app.config file. Doing this asynchronously in multiple AppDomains causes contention-issues, but that's a different topic!

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top