2010-10-14 3 views
133

Я пытаюсь реализовать пользовательский раздел конфигурации в проекте, и я продолжаю сталкиваться с исключениями, которые я не понимаю. Я надеюсь, что кто-то может заполнить пробелы здесь.Как реализовать 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 но обнаружил две вещи:

  1. это не работали
  2. это осуждается.

Теперь я полностью потерял, что делать, поэтому я могу читать свои данные из config. Любая помощь пожалуйста!

+0

Я не могу получить эту работу. Мне бы хотелось увидеть RT.Core.Config.ServicesSection. Я просто получаю Unrecognized element 'AddService', несмотря на использование кода из принятого ответа. – sirdank

+0

я пропустил это в первый тоже - эта часть: [ConfigurationCollection (TYPEOF (ServiceCollection), AddItemName = "добавить", ClearItemsName = "ясно", RemoveItemName = "удалить")] AddItemName должен соответствовать, так что если вам изменено «добавить» в «addService», это сработало бы – HeatherD

ответ

167

Предыдущий ответ правильный, но я дам вам весь код.

Ваш 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]; 
+8

. Свойства '[Добавить | Удалить | Очистить] ItemName' в атрибуте' ConfigurationCollection' в этом случае не нужны, потому что «add»/«clear»/«remove "уже являются именами XML-элементов по умолчанию. –

+2

Как я могу заставить его работать, чтобы теги не добавлялись? Кажется, что это работает, только если они добавляются. Это не сработает, если это был или JonathanWolfson

+7

@JonathanWolfson: просто измените AddItemName = "add" на AddItemName = «Сервис» –

4

Попробуйте наследовать от ConfigurationSection. Это пример blog post от Phil Haack.

Подтверждено, согласно документации IConfigurationSectionHandler:

В .NET Framework версии 2.0 и выше, вы должны вместо этого вывести из класса ConfigurationSection реализовать соответствующий обработчик раздела конфигурации.

43

Это общий код для сбора конфигурации:

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, вы можете просто использовать его в разделе конфигурации (это пример из мой грузоотправитель):

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"]; } 
    } 
} 

The Config Element является конфигурация Здесь:

конфигурационный файл будет выглядеть следующим образом:

<?xml version="1.0" encoding="utf-8" ?> 
    <dispatcherConfigurationSection> 
    <eventsDispatches> 
     <add name="Log" ></add> 
     <add name="Notification" ></add> 
     <add name="tester" ></add> 
    </eventsDispatches> 
    </dispatcherConfigurationSection> 

Надеюсь, что это поможет!

+0

Прохладный! Думал об этом и обнаружил, что я не один. Желание MS реализовать это для всех конфигураций FCL – abatishchev

+0

Любое предложение о том, как это сделать с помощью BasicMap для элементов? Я не хочу использовать Add, если я могу его избежать. – SpaceCowboy74

61

Если вы ищете пользовательский раздел конфигурации, как следующий

<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 сборки для вашего проекта

Посмотрите на каждый вложенный элемент, который я использовал, Первый - это учетные данные с двумя атрибутами, поэтому давайте сначала его добавим

Элемент учетных данных

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 является перечислением, и если вы попытаетесь использовать любое другое значение в конфигурации, которое не определено в приложении Enumeration, то при запуске будет выбрано 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; 

Я надеюсь, что выше пост поможет вам начните с немного сложного рода пользовательских конфигурационных разделов.

Счастливый Coding :)

**** **** Редактировать Включение 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; 
      } 
     } 

для люди, которые все еще путаются о том, как доходность действительно работает. this nice article

Два ключевых пункта, взятых из вышеприведенной статьи:

Это действительно не завершает выполнение метода. return return приостанавливает выполнение метода и в следующий раз, когда вы его вызываете (для следующего значения перечисления ), метод будет продолжать выполняться с последнего обратного вызова . Это звучит немного запутанно, я думаю ... (Shay Friedman)

Выход не является отличительной чертой .Net runtime. Это всего лишь функция C# , которая скомпилирована в простой код IL компилятором C#. (Lars Corneliussen)

+1

Спасибо за предоставление полного примера, это действительно помогает! –

11

Простая альтернатива для тех, кто предпочел бы не писать все эти шаблоны конфигурации вручную ...

1) Установить Nerdle.AutoConfig из NuGet

2) Определить тип ServiceConfig (либо конкретный класс или только интерфейс, либо будет делать)

public interface IServiceConfiguration 
{ 
    int Port { get; } 
    ReportType ReportType { get; } 
} 

3) Вам нужен тип для удерживайте коллекцию, например

public interface IServiceCollectionConfiguration 
{ 
    IEnumerable<IServiceConfiguration> Services { get; } 
} 

4) Добавить раздел конфигурации как так (примечание верблюжьего наименовании)

<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) Карта с AutoConfig

var services = AutoConfig.Map<IServiceCollectionConfiguration>(); 
+1

Слава Богу за этот ответ – Svend

Смежные вопросы