【问题标题】:ConfigurationElementCollection with a number of ConfigurationElements of different typeConfigurationElementCollection 具有许多不同类型的 ConfigurationElement
【发布时间】:2012-01-10 21:38:30
【问题描述】:

是否可以有一个 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”。

因为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() 永远不会被调用。

那么问题来了:如何让子类自行解析未知元素?

【问题讨论】:

    标签: c# .net app-config system.configuration custom-configuration


    【解决方案1】:

    我也调查了这个。 PolymorphicConfigurationElementCollection&lt;T&gt;seems deprecated。 编辑:不是,请参阅下面“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 的基类,您可以在其中定义一些共享属性。

    【讨论】:

    • 您已链接版本。 3.1,here's the recent 并且没有过时
    • 你是对的,改变了我的答案来反映这一点。感谢您的关注。
    【解决方案2】:

    来自 EntLib5 的Microsoft.Practices.EnterpriseLibrary.Common.Configuration.PolymorphicConfigurationElementCollection&lt;T&gt; 将这项工作作为一种魅力。

    【讨论】:

    • 我知道这个答案是不久前发布的,但是如果您将其标记为已接受的答案,您能否详细说明一下?
    • @goric:您需要继承它并覆盖RetrieveConfigurationElementType 以在运行时替换类型。另见NameTypeConfigurationElementCollection
    【解决方案3】:

    需要创建特定类型的实例(MyType1MyType2),以便子类自行解析未知元素或属性。

    由于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. 在元素上调用System.Configuration.ConfigurationElementinternal virtual void AssociateContext( BaseConfigurationRecord configRecord )方法。
    4. 在元素上调用System.Configuration.ConfigurationElementinternal void CallInit()方法。
    5. 返回准备好的元素。

    顺便说一句,如果不会有太多不同的集合元素,请考虑使用类似的东西:

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

    这样您就可以避免将项目从 MyCollection 转换为特定类型。

    【讨论】:

      猜你喜欢
      • 2010-11-01
      • 2013-02-16
      • 2014-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-01
      • 2020-01-08
      相关资源
      最近更新 更多