Пользовательский раздел конфигурации со свойствами, отличными от ConfigurationElement

У меня есть рабочий раздел пользовательской конфигурации. Однако получить мои данные через ConfigurationElementCollection очень сложно, но когда я пытаюсь реализовать свое свойство как IEnumerable, происходит сбой с ошибкой:

ConfigurationErrorsException не было обработано «Свойство 'contacts' не является ConfigurationElement».

Вот код, вызывающий сбой:

[ConfigurationProperty("contacts", IsDefaultCollection = false)]
public IEnumerable<string> Contacts
{
    get { return ((ContactCollection)base["contacts"]).Cast<ContactElement>().Select(x => x.Address); }
}

Однако, если я изменю его на это:

[ConfigurationProperty("contacts", IsDefaultCollection = false)]
public ContactCollection Contacts
{
    get { return ((ContactCollection)base["contacts"]); }
}

Все работает нормально. Этот ответ звучит так, как будто Microsoft решила, что это не разрешено, и поэтому я не могу иметь никаких свойств типов кроме ConfigurationElement. Это действительно так? Как я могу реализовать свою собственность как IEnumerable<string>?

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

<emergency>
    <contact address="[email protected]" />
    <contact address="[email protected]" />
</emergency>

Or

<emergency>
    <contact>[email protected]</contact>
    <contact>[email protected]</contact>
</emergency>

Спасибо!


person sirdank    schedule 05.07.2016    source источник
comment
Вы имеете мое сочувствие. Пользовательские разделы конфигурации с вложенными коллекциями — это огромная проблема. Я написал их много и до сих пор не могу вспомнить синтаксис достаточно ясно, чтобы дать ответ. Возможно, это сообщение поможет: //stackoverflow.com/questions/10958054/how-to-create-a-configuration-section-that-contains-a-коллекцию-коллекций   -  person Scott Hannen    schedule 05.07.2016
comment
Мне кажется, что большая часть сложности связана с поддержкой разделов, которые обновляются программно или с поддержкой переопределения файлов конфигурации более низкого уровня. Это замечательно, но на практике мы почти никогда не используем это. Можно с уверенностью сказать, что мы никогда не используем это.   -  person Scott Hannen    schedule 05.07.2016


Ответы (2)


Есть ли вред в предоставлении двух методов? Первый для выполнения требований Microsoft, а второй для выполнения ваших собственных требований.

    public IEnumerable<string> Contacts
    {
        get
        {
            return ContactCollection.Cast<ContactElement>().Select(x => x.Address);     
        }
    }

    [ConfigurationProperty("contacts", IsDefaultCollection = false)]
    public ContactCollection ContactCollection
    {
        get { return ((ContactCollection)base["contacts"]); }
    }
person Will    schedule 15.07.2016
comment
Я хотел бы избежать этого, но я не знаю, почему кто-то проголосовал за вас. Если это единственное решение, я приму его в будущем. Кроме того, спасибо за ваш первый ответ после пяти лет пребывания здесь! :) - person sirdank; 15.07.2016
comment
Обратите внимание, что вы даже можете объявить ContactCollection как частный, что интересно, если вы хотите замаскировать свойство от вызывающих абонентов и intellisense. - person Simon Mourier; 17.07.2016
comment
Лол ... мой первый ответ, и меня сразу же проголосовали! Я зря боялся ;) - person Will; 18.07.2016
comment
Хотя потребность в этом решении кажется мне глупой, с модификатором private он работает так же хорошо, как я мог надеяться. Спасибо! - person sirdank; 22.07.2016

Одним из решений, если вам нужен только список строк, является объявление списка значений, разделенных запятыми (или любым другим разделителем), например:

    [ConfigurationProperty("contacts")]
    [TypeConverter(typeof(StringSplitConverter))]
    public IEnumerable<string> Contacts
    {
        get
        {
            return (IEnumerable<string>)base["contacts"];
        }
    }

С этим классом TypeConverter:

public class StringSplitConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return string.Format("{0}", value).Split(',');
    }
}

Тогда ваш файл .config будет просто объявлен следующим образом:

<configuration>
  ...
  <mySection contacts="bill,joe" />
  ...
</configuration>

Обратите внимание, что это не работает для коллекций, когда вам всегда нужно явно объявлять свойство, как в ответе Уилла.

person Simon Mourier    schedule 17.07.2016