10

是否可以有一个 CollectionElementCollection,其中包含许多不同类型的 CollectionElement,例如:

<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”。

因为Type1SpecificPropMyType1not中定义MyElement,尤其是如果MyCollection有 next 方法:

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

即返回基类,因此OnDeserializeUnrecognizedAttribute()在子类中永远不会被调用。

那么问题来了:如何让子类自己解决未知元素呢?

4

3 回答 3

11

我也调查了这个。PolymorphicConfigurationElementCollection<T> 似乎已弃用。编辑:不是,请参阅下面“abatishchev”的评论,我只是在链接旧版本。

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 的基类,您可以在其中定义一些共享属性。

于 2012-01-09T21:46:02.790 回答
2

Microsoft.Practices.EnterpriseLibrary.Common.Configuration.PolymorphicConfigurationElementCollection<T>来自 EntLib5 做这项工作是一种魅力。

于 2011-12-21T08:26:02.887 回答
1

需要创建特定类型 ( MyType1and )的实例,以便子类自行解析未知元素或属性。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为特定类型。

于 2011-02-20T00:59:29.663 回答