ConfigurationElementCollection с несколькими элементами ConfigurationElement другого типа - PullRequest
10 голосов
/ 04 февраля 2011

Возможно ли иметь коллекцию CollectionElementCollection с несколькими различными по типу CollectionElements, например ::

<collection>
    <add type="MyType1, MyLib" Type1SpecificProp="1" />
    <add type="MyType2, MyLib" Type2SpecificProp="2" />
</collection

У меня есть все классы, необходимые для такого решения:

class MyCollection : ConfigurationElementCollection { }
class MyElement : ConfigurationElement { }
class MyType1 : MyElement { }
class MyType2 : MyElement { }
...
etc

но при запуске приложения я получаю следующую предсказуемую ошибку:

Нераспознанный атрибут 'Type1SpecificProp'.

потому что Type1SpecificProp определен в MyType1, а не MyElement, особенно если MyCollection имеет следующий метод:

protected override ConfigurationElement CreateNewElement()
{
    return new MyElement(); // but I want instantiate not the base class but by a type given
}

т.е. возвращает базовый класс, таким образом, OnDeserializeUnrecognizedAttribute() в дочернем классе никогда не вызывается.

Таким образом, вопрос заключается в следующем: как позволить дочерним классам самостоятельно разрешать неизвестные элементы?

Ответы [ 3 ]

11 голосов
/ 10 января 2012

Я тоже посмотрел на это. PolymorphicConfigurationElementCollection<T> кажется устаревшим . Изменить: это не так, см. Комментарий «Абатищев» ниже, я просто связывал старую версию.

Решение Rest Wing было многообещающим, но, к сожалению, требовало вызова внутренних методов, находящихся в другом пространстве имен. Хотя это возможно с помощью размышлений, в этом случае он не получит цены за кодирование красоты.

Я тоже покопался в источнике с Reflection и нашел следующее решение:

[ConfigurationCollection(typeof(ElementBaseConfig), CollectionType=ConfigurationElementCollectionType.BasicMap)]
public class MyTypesConfigCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        // Not used but function must be defined
        return null;
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return element;
    }

    protected override ConfigurationElement CreateNewElement(string elementName)
    {
        switch (elementName)
        {
            case "mytype1":
                return new MyType1Config();

            case "mytype2":
                return new MyType2Config();

            default:
                throw new ConfigurationErrorsException(
                    string.Format("Unrecognized element '{0}'.", elementName));
        }
    }

    protected override bool IsElementName(string elementName)
    {
        // Required to be true
        return true;
    }

    public override ConfigurationElementCollectionType CollectionType
    {
        get { return ConfigurationElementCollectionType.BasicMap; }
    }
}

Требуется переопределение CollectionType, даже если это было указано с помощью атрибута в верхней части. Если не переопределить, базовый класс «CollectionType» все еще ссылается на «AddRemoveClearMap», который не будет запускать требуемую функцию «CreateNewElement (string elementName)», но это вариант без параметров «CreateNemElement ()». По той же причине перезаписанная функция IsElementName должна возвращать true.

Обратите внимание, что я создал ElementBaseConfig, который является базовым классом MyType1Config и MyType2Config, в котором вы можете определить некоторые общие атрибуты.

2 голосов
/ 21 декабря 2011

Microsoft.Practices.EnterpriseLibrary.Common.Configuration.PolymorphicConfigurationElementCollection<T> из EntLib5 делают эту работу как заклинание.

1 голос
/ 20 февраля 2011

Необходимо создать экземпляр определенного типа (MyType1 и MyType2), чтобы дочерние классы могли самостоятельно разрешать неизвестные элементы или атрибуты.

Поскольку метод CreateNewElement не даетлюбая информация об атрибутах элемента, это не место, где может происходить конкретная реализация типа.

После некоторого копания с помощью Reflector, получается следующий частичный стек вызовов:

VariantCollection.MyCollection.CreateNewElement()
System.Configuration.ConfigurationElementCollection.CallCreateNewElement()
System.Configuration.ConfigurationElementCollection.OnDeserializeUnrecognizedElement(string elementName, XmlReader reader)

OnDeserializeUnrecognizedElement метод может быть переопределен в MyCollection классе для создания экземпляра определенного типа.Вместо использования метода CallCreateNewElement без параметров используйте новый метод, который получает XmlReader:

  1. Считать атрибут type (обеспечить его существование и действительность).
  2. Создать новый элементуказанного типа.
  3. Вызов internal virtual void AssociateContext( BaseConfigurationRecord configRecord ) метода System.Configuration.ConfigurationElement для элемента.
  4. Вызов internal void CallInit() метода System.Configuration.ConfigurationElement для элемента.
  5. Возвратподготовленный элемент.

Кстати, если не будет слишком много различных элементов коллекции, рассмотрите возможность использования чего-то вроде:

<myType1Collection>
    <add Type1SpecificProp="1" />
</myType1Collection>
<myType2Collection>
    <add Type2SpecificProp="2" />
</myType2Collection>

Таким образом, вы можете избежать приведения предметов из MyCollection к конкретному типу.

...