Как реализовать ConfigurationSection с помощью ConfigurationElementCollection
Я пытаюсь реализовать пользовательский раздел конфигурации в проекте, и я продолжаю сталкиваться с исключениями, которые я не понимаю. Я надеюсь, что кто-то может заполнить пробелы здесь.
я имею App.config
это выглядит так:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="ServicesSection" type="RT.Core.Config.ServicesConfigurationSectionHandler, RT.Core"/>
</configSections>
<ServicesSection type="RT.Core.Config.ServicesSection, RT.Core">
<Services>
<AddService Port="6996" ReportType="File" />
<AddService Port="7001" ReportType="Other" />
</Services>
</ServicesSection>
</configuration>
у меня есть ServiceConfig
элемент определяется так:
public class ServiceConfig : ConfigurationElement
{
public ServiceConfig() {}
public ServiceConfig(int port, string reportType)
{
Port = port;
ReportType = reportType;
}
[ConfigurationProperty("Port", DefaultValue = 0, IsRequired = true, IsKey = true)]
public int Port
{
get { return (int) this["Port"]; }
set { this["Port"] = value; }
}
[ConfigurationProperty("ReportType", DefaultValue = "File", IsRequired = true, IsKey = false)]
public string ReportType
{
get { return (string) this["ReportType"]; }
set { this["ReportType"] = value; }
}
}
И у меня есть ServiceCollection
определяется так:
public class ServiceCollection : ConfigurationElementCollection
{
public ServiceCollection()
{
Console.WriteLine("ServiceCollection Constructor");
}
public ServiceConfig this[int index]
{
get { return (ServiceConfig)BaseGet(index); }
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
public void Add(ServiceConfig serviceConfig)
{
BaseAdd(serviceConfig);
}
public void Clear()
{
BaseClear();
}
protected override ConfigurationElement CreateNewElement()
{
return new ServiceConfig();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((ServiceConfig) element).Port;
}
public void Remove(ServiceConfig serviceConfig)
{
BaseRemove(serviceConfig.Port);
}
public void RemoveAt(int index)
{
BaseRemoveAt(index);
}
public void Remove(string name)
{
BaseRemove(name);
}
}
Часть, которую я пропускаю, это то, что нужно сделать для обработчика. Первоначально я пытался реализовать IConfigurationSectionHandler
но нашел две вещи:
- это не сработало
- Это устарело.
Теперь я совершенно заблудился, что делать, поэтому я могу читать свои данные из конфига. Любая помощь, пожалуйста!
5 ответов
Предыдущий ответ правильный, но я дам вам весь код.
Ваш app.config должен выглядеть так:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="ServicesSection" type="RT.Core.Config.ServiceConfigurationSection, RT.Core"/>
</configSections>
<ServicesSection>
<Services>
<add Port="6996" ReportType="File" />
<add Port="7001" ReportType="Other" />
</Services>
</ServicesSection>
</configuration>
Ваш ServiceConfig
а также ServiceCollection
классы остаются неизменными.
Вам нужен новый класс:
public class ServiceConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("Services", IsDefaultCollection = false)]
[ConfigurationCollection(typeof(ServiceCollection),
AddItemName = "add",
ClearItemsName = "clear",
RemoveItemName = "remove")]
public ServiceCollection Services
{
get
{
return (ServiceCollection)base["Services"];
}
}
}
И это должно сработать. Для его потребления вы можете использовать:
ServiceConfigurationSection serviceConfigSection =
ConfigurationManager.GetSection("ServicesSection") as ServiceConfigurationSection;
ServiceConfig serviceConfig = serviceConfigSection.Services[0];
Если вы ищете пользовательский раздел конфигурации, как показано ниже
<CustomApplicationConfig>
<Credentials Username="itsme" Password="mypassword"/>
<PrimaryAgent Address="10.5.64.26" Port="3560"/>
<SecondaryAgent Address="10.5.64.7" Port="3570"/>
<Site Id="123" />
<Lanes>
<Lane Id="1" PointId="north" Direction="Entry"/>
<Lane Id="2" PointId="south" Direction="Exit"/>
</Lanes>
</CustomApplicationConfig>
тогда вы можете использовать мою реализацию раздела конфигурации, чтобы начать добавлять System.Configuration
сборка ссылка на ваш проект
Посмотрите на каждый вложенный элемент, который я использовал. Первый - это Credentials с двумя атрибутами, поэтому давайте сначала добавим его.
Элемент учетных данных
public class CredentialsConfigElement : System.Configuration.ConfigurationElement
{
[ConfigurationProperty("Username")]
public string Username
{
get
{
return base["Username"] as string;
}
}
[ConfigurationProperty("Password")]
public string Password
{
get
{
return base["Password"] as string;
}
}
}
PrimaryAgent и SecondaryAgent
Оба имеют одинаковые атрибуты и выглядят как адреса для набора серверов для основного и аварийного переключения, поэтому вам просто нужно создать один класс элементов для обоих из них, как показано ниже
public class ServerInfoConfigElement : ConfigurationElement
{
[ConfigurationProperty("Address")]
public string Address
{
get
{
return base["Address"] as string;
}
}
[ConfigurationProperty("Port")]
public int? Port
{
get
{
return base["Port"] as int?;
}
}
}
Позже в этом посте я объясню, как использовать два разных элемента с одним классом, давайте пропустим SiteId, поскольку в нем нет различий. Вам просто нужно создать один класс, такой же, как указано выше, только с одним свойством. давайте посмотрим, как реализовать коллекцию Lanes
он разделен на две части: сначала нужно создать класс реализации элемента, а затем создать класс элемента коллекции.
LaneConfigElement
public class LaneConfigElement : ConfigurationElement
{
[ConfigurationProperty("Id")]
public string Id
{
get
{
return base["Id"] as string;
}
}
[ConfigurationProperty("PointId")]
public string PointId
{
get
{
return base["PointId"] as string;
}
}
[ConfigurationProperty("Direction")]
public Direction? Direction
{
get
{
return base["Direction"] as Direction?;
}
}
}
public enum Direction
{
Entry,
Exit
}
вы можете заметить, что один атрибут LanElement
является перечислением, и если вы попытаетесь использовать любое другое значение в конфигурации, которое не определено в приложении перечисления, будет выброшено System.Configuration.ConfigurationErrorsException
на старте. Хорошо, давайте перейдем к определению коллекции
[ConfigurationCollection(typeof(LaneConfigElement), AddItemName = "Lane", CollectionType = ConfigurationElementCollectionType.BasicMap)]
public class LaneConfigCollection : ConfigurationElementCollection
{
public LaneConfigElement this[int index]
{
get { return (LaneConfigElement)BaseGet(index); }
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}
public void Add(LaneConfigElement serviceConfig)
{
BaseAdd(serviceConfig);
}
public void Clear()
{
BaseClear();
}
protected override ConfigurationElement CreateNewElement()
{
return new LaneConfigElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((LaneConfigElement)element).Id;
}
public void Remove(LaneConfigElement serviceConfig)
{
BaseRemove(serviceConfig.Id);
}
public void RemoveAt(int index)
{
BaseRemoveAt(index);
}
public void Remove(String name)
{
BaseRemove(name);
}
}
Вы можете заметить, что я установил AddItemName = "Lane"
Вы можете выбрать все, что вам нравится для вашей записи в коллекции, я предпочитаю использовать "добавить" по умолчанию, но я изменил его только ради этого поста.
Теперь все наши вложенные элементы были реализованы, теперь мы должны объединить все те в классе, который должен реализовать System.Configuration.ConfigurationSection
CustomApplicationConfigSection
public class CustomApplicationConfigSection : System.Configuration.ConfigurationSection
{
private static readonly ILog log = LogManager.GetLogger(typeof(CustomApplicationConfigSection));
public const string SECTION_NAME = "CustomApplicationConfig";
[ConfigurationProperty("Credentials")]
public CredentialsConfigElement Credentials
{
get
{
return base["Credentials"] as CredentialsConfigElement;
}
}
[ConfigurationProperty("PrimaryAgent")]
public ServerInfoConfigElement PrimaryAgent
{
get
{
return base["PrimaryAgent"] as ServerInfoConfigElement;
}
}
[ConfigurationProperty("SecondaryAgent")]
public ServerInfoConfigElement SecondaryAgent
{
get
{
return base["SecondaryAgent"] as ServerInfoConfigElement;
}
}
[ConfigurationProperty("Site")]
public SiteConfigElement Site
{
get
{
return base["Site"] as SiteConfigElement;
}
}
[ConfigurationProperty("Lanes")]
public LaneConfigCollection Lanes
{
get { return base["Lanes"] as LaneConfigCollection; }
}
}
Теперь вы можете видеть, что у нас есть два свойства с именем PrimaryAgent
а также SecondaryAgent
оба имеют один и тот же тип, теперь вы можете легко понять, почему у нас был только один класс реализации для этих двух элементов.
Прежде чем вы сможете использовать этот недавно изобретенный раздел конфигурации в вашем app.config (или web.config), вам просто нужно сообщить приложению, что вы изобрели свой собственный раздел конфигурации, и дать ему некоторое уважение, для этого вам нужно добавить следующие строки в app.config (может быть сразу после запуска корневого тега).
<configSections>
<section name="CustomApplicationConfig" type="MyNameSpace.CustomApplicationConfigSection, MyAssemblyName" />
</configSections>
ПРИМЕЧАНИЕ. Имя MyAssemblyName должно быть без.dll, например, если имя файла сборки - myDll.dll, используйте myDll вместо myDll.dll.
чтобы получить эту конфигурацию, используйте следующую строку кода в любом месте вашего приложения
CustomApplicationConfigSection config = System.Configuration.ConfigurationManager.GetSection(CustomApplicationConfigSection.SECTION_NAME) as CustomApplicationConfigSection;
Надеюсь, что вышеприведенный пост поможет вам начать с немного сложного вида пользовательских разделов конфигурации.
Удачного кодирования:)
**** Редактировать ****, чтобы включить LINQ на LaneConfigCollection
Вы должны реализовать IEnumerable<LaneConfigElement>
И добавить следующую реализацию GetEnumerator
public new IEnumerator<LaneConfigElement> GetEnumerator()
{
int count = base.Count;
for (int i = 0; i < count; i++)
{
yield return base.BaseGet(i) as LaneConfigElement;
}
}
для людей, которые все еще не понимают, как действительно работает yield, прочитайте эту хорошую статью
Два ключевых пункта, взятых из вышеприведенной статьи:
это действительно не заканчивает выполнение метода. yield return приостанавливает выполнение метода, и при следующем вызове его (для следующего значения перечисления) метод продолжит выполнение с последнего вызова yield return. Это звучит немного запутанно, я думаю... (Шей Фридман)
Выход не является особенностью среды выполнения.Net. Это просто особенность языка C#, которая компилируется в простой код IL компилятором C#. (Ларс Корнелиусен)
Это общий код для сбора конфигурации:
public class GenericConfigurationElementCollection<T> : ConfigurationElementCollection, IEnumerable<T> where T : ConfigurationElement, new()
{
List<T> _elements = new List<T>();
protected override ConfigurationElement CreateNewElement()
{
T newElement = new T();
_elements.Add(newElement);
return newElement;
}
protected override object GetElementKey(ConfigurationElement element)
{
return _elements.Find(e => e.Equals(element));
}
public new IEnumerator<T> GetEnumerator()
{
return _elements.GetEnumerator();
}
}
После того, как у вас есть GenericConfigurationElementCollection
Вы можете просто использовать его в разделе конфигурации (это пример из моего Dispatcher):
public class DispatcherConfigurationSection: ConfigurationSection
{
[ConfigurationProperty("maxRetry", IsRequired = false, DefaultValue = 5)]
public int MaxRetry
{
get
{
return (int)this["maxRetry"];
}
set
{
this["maxRetry"] = value;
}
}
[ConfigurationProperty("eventsDispatches", IsRequired = true)]
[ConfigurationCollection(typeof(EventsDispatchConfigurationElement), AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")]
public GenericConfigurationElementCollection<EventsDispatchConfigurationElement> EventsDispatches
{
get { return (GenericConfigurationElementCollection<EventsDispatchConfigurationElement>)this["eventsDispatches"]; }
}
}
Элемент конфигурации является config Здесь:
public class EventsDispatchConfigurationElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get
{
return (string) this["name"];
}
set
{
this["name"] = value;
}
}
}
Файл конфигурации будет выглядеть так:
<?xml version="1.0" encoding="utf-8" ?>
<dispatcherConfigurationSection>
<eventsDispatches>
<add name="Log" ></add>
<add name="Notification" ></add>
<add name="tester" ></add>
</eventsDispatches>
</dispatcherConfigurationSection>
Надеюсь, это поможет!
Более простая альтернатива для тех, кто предпочел бы не писать все эти шаблоны конфигурации вручную...
1) Установите Nerdle.AutoConfig из NuGet
2) Определите ваш тип ServiceConfig (подойдет либо конкретный класс, либо просто интерфейс)
public interface IServiceConfiguration
{
int Port { get; }
ReportType ReportType { get; }
}
3) Вам понадобится тип для хранения коллекции, например
public interface IServiceCollectionConfiguration
{
IEnumerable<IServiceConfiguration> Services { get; }
}
4) Добавьте раздел конфигурации следующим образом (обратите внимание на именование camelCase)
<configSections>
<section name="serviceCollection" type="Nerdle.AutoConfig.Section, Nerdle.AutoConfig"/>
</configSections>
<serviceCollection>
<services>
<service port="6996" reportType="File" />
<service port="7001" reportType="Other" />
</services>
</serviceCollection>
5) Карта с автоконфигурацией
var services = AutoConfig.Map<IServiceCollectionConfiguration>();
Попробуйте наследовать от ConfigurationSection. В этом посте Фила Хаака есть пример.
Подтверждено согласно документации для IConfigurationSectionHandler:
В.NET Framework версии 2.0 и выше вы должны вместо этого наследовать от класса ConfigurationSection для реализации соответствующего обработчика раздела конфигурации.