Объединение настраиваемых разделов конфигурации во время выполнения в .NET

Мой вопрос касается работы со стандартными объектами конфигурации .NET и настраиваемыми элементами конфигурации (которые можно определить, расширив _ 1_ класс). Обычно мы получаем их из класса System.Configuration.ConfigurationManager методы, пример которых GetSection(...) .

Загруженный объект конфигурации выглядит как объединенный объект конфигурации, содержащий настройки, присутствующие в файле конфигурации приложения (app.config или web.config файл, который, возможно, создал разработчик), и то, что определено в файле machine.config (последний идет с установкой .NET Framework).

Итак, мы можем предположить, что конфигурация загружается иерархически, сначала с помощью machine.config и любой пользовательской конфигурации, накладывающейся на эту настройку по умолчанию, и на нее можно смотреть так:

  • machine.config
    • app.config (overrides / merges with respective elements found in machine.config )

Моя цель - создать несколько уровней конфигурации, чтобы между (и, если возможно, после) файла machine.config и app.config могли быть другие файлы конфигурации. :

  • machine.config
    • custom.config (a custom config interfering between the machine.config and the app.config file)
      • app.config - now app.config is merged with both machine.config and custom.config

Обновить

Дело в том, что если я определил раздел конфигурации как в custom.config, так и в app.config, мне нужно получить объединенную версию обеих конфигураций, когда я вызываю ConfigurationManager.GetSection("MyCustomSection"). В идеале было бы здорово, если бы я смог выполнить слияние, как описано в этом MSDN статья.


Обычно я пишу свой собственный класс диспетчера конфигурации и стараюсь продвинуться как можно дальше, чтобы получить желаемый результат, но при условии, что в .NET framework это работает для machine.config и app.config, я подумал, что мне может пригодиться встроенная функциональность фреймворка. Кроме того, я не знаю, как вручную запустить такое слияние, если я действительно должен прибегать к собственной реализации диспетчера конфигураций.

Итак, можно ли использовать встроенные механизмы слияния раздела / элемента конфигурации с пользовательскими файлами конфигурации? Я особенно заинтересован в разработке и поддержке этого для пользовательских разделов конфигурации. Пространство имен System.Configuration содержит базовые объекты для создания раздела и элементов конфигурации, которые позволяют выполнять некоторые настройки, связанные с объединением (например, установка соответствующего _ 6_, например). Связаны ли они только со слиянием с machine.config (или несколькими уровнями файлов web.config в веб-приложении), или можно вручную запустить слияние предварительно -загруженные файлы конфигурации? Я пытаюсь избежать поддержки пользовательского слияния в любом из моих настраиваемых объектов конфигурации и, вероятно, забываю о поддержке существующих настроек из System.Configuration ...


Обновить

Я хотел бы сделать важное уточнение в ответ на существующие ответы и комментарии. Я могу загрузить объекты ConfigurationSection из текущей настройки приложения (app.config / web.config) и из физического файла, который является моим custom.config . Мне нужно знать, есть ли возможность объединить эти объекты, не прибегая к отражению и сравнению свойств по свойствам, с помощью некоторых встроенных средств в структуре.


Примечание. Я был бы признателен за лучшее решение, которое будет работать на .NET 3.0+. Добавьте примечание, если ваш ответ нацелен на более высокую версию платформы.


person Ivaylo Slavov    schedule 04.05.2015    source источник
comment
Я с нетерпением жду ответов на этот вопрос. Но я боюсь, что то, о чем вы просите, невозможно. Система конфигурации .NET не является сверхгибкой, когда дело доходит до разделения / модуляции файла конфигурации.   -  person stakx - no longer contributing    schedule 08.05.2015
comment
@stakx, может ты и прав. Тем не менее, поскольку сама платформа .NET предоставляет возможности слияния (и выполняет это слияние внутри), должна быть возможность хотя бы понять, как все это происходит. Мне удалось многое сделать для упрощения конфигурации в большинстве моих проектов, но все же эта возможность слияния - это недостающий фрагмент головоломки, которую я давно ищу.   -  person Ivaylo Slavov    schedule 08.05.2015
comment
Это не точный ответ на вопрос, но вы можете использовать внешние файлы конфигурации спутников с атрибутом configSource: msdn.microsoft.com/library/   -  person Simon Mourier    schedule 09.05.2015
comment
@SimonMourier, спасибо за подсказку. Однако это, похоже, помогает разбить файл конфигурации на более удобные места (если я чего-то не упускаю). Меня больше интересует наслоение настроек между разными файлами конфигурации с аналогичной структурой - например, если и app.config, и custom.config определили элемент custom element, мне нужно получить его объединенную версию из обоих файлов. когда я использую ConfigurationManager.GetSection(...). Надеюсь, это немного больше проясняет мои требования.   -  person Ivaylo Slavov    schedule 11.05.2015


Ответы (5)


Система конфигурации .NET не очень гибкая

Этот комментарий является точным и объясняет, почему вы так долго искали и еще ничего не нашли. Не все части .NET Framework "хороши", System.Configuration заслуживает места в самом низу. Он смехотворно перестроен для чего-то, что в конечном итоге является простой задачей, но в то же время превратилось в нечто чрезвычайно негибкое. Трудно реконструировать, как это произошло, я думаю, что все было парализовано соображениями безопасности. Возможно, в некоторой степени понятно, что использование данных в программе всегда сопряжено с серьезным риском.

Единственная точка расширения, о которой я знаю, - это написание собственного SettingsProvider. Фреймворк имеет только один для общего использования - класс LocalFileSettingProvider. Также чрезвычайно негибкий, нет никакого способа изменить его поведение. Существует достойный пример для поставщика настраиваемых параметров: Образец RegistrySettingsProvider демонстрирует поставщика, который хранит настройки в реестре. Это может быть хорошей отправной точкой для написания собственного.

Не совсем то, что вы имеете в виду, возможно, не стоит забывать о том, что вы можете разбить слои внутри System.Configuration.

person Community    schedule 08.05.2015
comment
Спасибо за честный ответ, и я ценю ссылку на образец, я изучу ее. Признаюсь, я немного разочарован, узнав, что конфигурация системы действительно такая негибкая. Казалось, что в этом действительно есть потенциал для достижения моих целей, но как будто мне было трудно добиться этого. Я бы сейчас подумал об альтернативах, если бы я не был таким упрямым ... но это уже другая сказка - person Ivaylo Slavov; 09.05.2015
comment
Я обновил свой пост, кстати. Имеет ли значение сценарий, о котором я сейчас говорю, или это все еще невозможно. - person Ivaylo Slavov; 09.05.2015
comment
Хмя, вы очень быстро добираетесь до точки, когда уже нет смысла использовать это вообще. Самостоятельный поиск конкретного файла .config - это то, чем вы никогда по-настоящему не захотите. - person Hans Passant; 09.05.2015

Как указано в silver, хорошо сконфигурированный 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>

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)
  • Нет необходимости в отражении или другой черной магии, чтобы вызвать такое поведение.

минусы

  • Не очень уверен, но, устанавливая map.MachineConfigFilename = PathToCustomConfig;, я предполагаю, что удаляю все значения, которые установлены настоящим machine.config файлом. Это может быть подвержено ошибкам, и этого следует избегать для веб-приложений, поскольку большинство из них полагаются на то, что находится на самом деле machine.config
  • Необходимо передать расположение файла конфигурации приложения, так как оно больше не определяется автоматически. Таким образом, необходимо выяснить, как будет называться app.config при компиляции кода (обычно AssemblyName.exe.config)
  • Таким образом можно объединить содержимое только двух файлов. Если нужна более крупная иерархия, это не сработает.

Я все еще нахожусь в процессе доработки техники, поэтому я вернусь, чтобы обновить этот пост, как только закончу.

person Community    schedule 14.05.2015
comment
Как это работает с appSettings? Я получаю неверную ошибку приведения при попытке загрузить настройки приложения. Есть предположения? - person Sri Reddy; 30.06.2017

По умолчанию существует 3 уровня наследования конфигурации: Machine, Exe и User (которые могут быть Roaming или Local). Если вы загружаете файлы конфигурации самостоятельно, вы можете использовать ExeConfigurationFileMap в сочетании с ConfigurationManager.OpenMappedExeConfiguration, чтобы загрузить собственную иерархию пользовательской конфигурации.

Я не думаю, что вы можете изменить с помощью этого пути по умолчанию для класса ConfigurationManager, но вы получите элемент Configuration, который можно использовать для получения любых разделов из загруженной иерархии конфигураций.

Если вы ознакомитесь с ответом на Как читать configSections, он включает некоторые примечания по определению, на каком уровне разделы были объявлены в иерархии (с помощью SectionInformation)

var localSections = cfg.Sections.Cast<ConfigurationSection>()
       .Where(s => s.SectionInformation.IsDeclared);
person Arkaine55    schedule 11.05.2015
comment
Хотя это не отвечает на мой вопрос, я должен поблагодарить вас за подсказку - если я могу определить, какие разделы были объявлены в custom.config и app.config (кажется, ваш ответ достаточно хорошо относится к этой части), у меня уже будут точные объекты раздела, которые мне нужно объединить. Теперь я должен найти способ вызвать это ... слияние. - person Ivaylo Slavov; 11.05.2015
comment
Плохо, похоже, вы на самом деле отвечаете на мой вопрос косвенно, поскольку ExeConfigurationFileMap действительно был подходящим вариантом, просто казалось немного неправильным (ab) использовать его так, как я хотел. Тем не менее, я боюсь, что вряд ли найду альтернативу, и вы заслуживаете некоторой похвалы или указываете мне на это. - person Ivaylo Slavov; 18.05.2015

Взгляните на следующий код, который знает, как загрузить ваши конфигурации или конфигурацию 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);

}
person silver    schedule 14.05.2015
comment
Привет, кажется, твоя идея приблизила меня к тому, чего я хочу. Ознакомьтесь с моим сообщением в вики по этому вопросу. Я был бы рад, если бы вы смогли подробнее рассказать о некоторых минусах - особенно, если я теряю то, что находится в machine.config, и если я могу объединить более двух файлов. Я пытался работать с RoamingUserConfigFilename, но это не помогло. - person Ivaylo Slavov; 14.05.2015
comment
если у вас один и тот же ключ в конфигурации компьютера и в конфигурации приложения, ваше приложение получит значения вашего приложения. вы можете использовать: ConfigurationManager.AppSettings [keyName]; значения, которые у вас есть только в конфигурации машины, будут доступны и после такого слияния. - person silver; 14.05.2015
comment
Дело в том, что когда я создаю ExeConfigurationFileMap, я прямо говорю map.MachineConfigFilename = PathToCustomConfig. Дело в том, что конфигурация exe объединена с тем, что я называю конфигурацией машины. Разве это не должно игнорировать реальный machine.config, или я что-то упускаю? - person Ivaylo Slavov; 14.05.2015
comment
вы правы, это не имеет смысла, я не знаю, почему это работает, но это так, значения, которые существуют в machine.config и не отображаются в ваших конфигурациях, будут доступны после этого переопределения. (Я также пробовал использовать другой конструктор для ExeConfigurationFileMap), пожалуйста, дайте мне знать, если вы найдете причину, по которой он работает. - person silver; 17.05.2015
comment
Я попытаюсь выяснить, почему это так, но я безумно догадываюсь, что существует внутренняя логика .NET, которая всегда учитывает конфигурацию машины. Я полагаю, что есть настройки, которые не предназначены для доступа через пользовательские файлы конфигурации приложений / веб, и без них .NET framework может не работать. На самом деле, я рад, что это все еще принимается во внимание, поскольку я мог бы извлечь выгоду из этого подхода и в веб-приложении. - person Ivaylo Slavov; 17.05.2015

нам может потребоваться использовать классы ConfigurationSection и ConfigurationProperty

Пожалуйста, обратитесь к ссылке ниже для получения подробной информации о реализации https://blog.danskingdom.com/adding-and-accessing-custom-sections-in-your-c-app-config/

person NidhinSPradeep    schedule 04.09.2020