Объединение пользовательских разделов конфигурации во время выполнения в .NET.
Мой вопрос касается работы со стандартными объектами конфигурации .NET и настраиваемыми элементами конфигурации (что можно определить, расширив класс System.Configuration.ConfigurationSection
). Обычно мы получаем их из методов System.Configuration.ConfigurationManager
, GetSection(...)
.
Загруженный объект конфигурации представляет собой объединенный объект конфигурации, содержащий настройку, присутствующую в файле конфигурации приложения (файл app.config или web.config, который разработчик) и то, что определено в файле machine.config (последний поставляется с установкой .NET Framework).
Итак, мы можем предположить, что конфигурация загружается иерархически с помощью machine.config, и любая пользовательская конфигурация, накладывающаяся на эту настройку по умолчанию, может выглядеть следующим образом:
- machine.config
- app.config (переопределяет/объединяет с соответствующими элементами, найденными в machine.config)
Моя цель - создать несколько уровней конфигурации, чтобы между (и, если возможно, после) могли быть другие файлы конфигурации между machine.config и app.config файл:
- machine.config
- custom.config (настраиваемая конфигурация, мешающая файлу machine.config и файлу app.config)
- app.config - теперь app.config объединяется с машинным .config и custom.config
Обновить
Точка с этим - если я определил раздел конфигурации как custom.config, так и app.config, мне нужно получить объединенную версию обеих конфигураций когда я звоню ConfigurationManager.GetSection("MyCustomSection")
. В идеале, будет здорово, если я смогу выполнить слияние, как описано в этой статье MSDN.
Обычно я пишу свой собственный класс диспетчера конфигурации и стараюсь как можно дойти до желаемого результата, но предполагая, что платформа .NET работает в machine.config и app.config, я подумал, что я мог бы использовать встроенные функции фреймворка. Кроме того, я не знаю, как вручную запускать такое слияние, если я действительно должен прибегать к реализации моего конфигурационного менеджера.
Итак, можно ли использовать встроенные механизмы раздела конфигурации/элемента, слияния с настраиваемыми файлами конфигурации? Я особенно заинтересован в разработке и поддержке этого для пользовательских разделов конфигурации. Пространство имен System.Configuration
содержит базовые объекты для создания раздела конфигурации и элементов, и это позволяет некоторые параметры, связанные с объединением (например, установка соответствующего ConfigurationElementCollectionType
). Связаны ли они только слиянием с machine.config (или несколькими слоями файлов web.config в веб-приложении), или же можно вручную инициировать слияние pre загруженные файлы конфигурации? Я стараюсь избегать поддержки пользовательского слияния в любом из моих настраиваемых объектов конфигурации и, возможно, забыть о поддержке существующих настроек из System.Configuration...
Обновление
Есть важное разъяснение, которое я хотел бы сделать в ответ на существующие ответы и комментарии. Я могу загрузить объекты ConfigurationSection
из текущей настройки приложения (app.config/ web.config) и из физического файла, который является моим обычным .config. Мне нужно знать, есть ли возможность объединить эти объекты, не прибегая к анализу и сопоставлению свойств по свойствам, некоторыми встроенными средствами в рамках.
Примечание.. Я был бы признателен за лучшее решение, которое будет работать на .NET 3.0+. Добавьте примечание, если ваш ответ нацелен на более высокую версию фреймворка.
Ответы
Ответ 1
Как отметил 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)
- Таким образом вы можете объединить содержимое двух файлов. Если нужна более высокая иерархия, тогда это не будет работать.
Я все еще в процессе совершенствования техники, поэтому я вернусь, чтобы обновить этот пост после его завершения.
Ответ 2
Конфигурационная система .NET не является супер гибкой
Этот комментарий называет его и объясняет, почему вы долго искали и ничего не нашли. Не все части .NET Framework являются "хорошими", System.Configuration заслуживает место на далеком дне. Это смехотворно перепроектировано для чего-то, что в конечном счете является простой задачей, но в то же время превратилось во что-то чрезвычайно негибкое. Трудно перепроектировать, как это произошло, я думаю, что он был парализован проблемами безопасности. Понятно, что, возможно, командование программой с данными всегда является значительным риском.
Единственная точка расширения, о которой я знаю, - это написать собственный SettingsProvider. Рамка имеет только один для общего использования - класс LocalFileSettingProvider. Также чрезвычайно негибкий, нет никакого способа изменить его поведение. Для поставщика настраиваемых параметров есть достойный пример, образец реестра RegistrySettingsProvider демонстрирует поставщика, который хранит настройки в реестре. Это может быть хорошей отправной точкой для написания собственных.
Не совсем то, что вы имеете в виду, возможно, поцарапайте идею о том, что вы можете пробиться в слоирование внутри System.Configuration.
Ответ 3
По умолчанию на самом деле три уровня наследования конфигурации: Machine, Exe и User (которые могут быть роуминг или локальные). Если вы загружаете файлы конфигурации самостоятельно, вы можете использовать класс ExeConfigurationFileMap в сочетании с ConfigurationManager.OpenMappedExeConfiguration, чтобы загрузить свою собственную конфигурационную иерархию.
Я не думаю, что вы можете изменить, где пути по умолчанию для класса ConfigurationManager с этим, но вы получите элемент конфигурации, который можно использовать для получения любых разделов из загруженной конфигурации конфигурации.
Если вы посмотрите ответ на Как читать конфигурационные файлы, он содержит некоторые примечания по определению того, на каких уровнях были объявлены разделы в иерархии (используя разделInformation)
var localSections = cfg.Sections.Cast<ConfigurationSection>()
.Where(s => s.SectionInformation.IsDeclared);
Ответ 4
посмотрите следующий код, который знает, как загрузить ваши конфигурации или конфигурацию 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);
}