9

是否有可能具有CollectionElementCollection与许多不同的由式CollectionElements,例如:ConfigurationElementCollection中与许多不同类型的ConfigurationElements的

<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 

,但是当我开始我的应用程序我得到未来可预见的错误:

Unrecognized attribute 'Type1SpecificProp'.

因为Type1SpecificPropMyType1MyElement定义,特别是如果MyCollection具有下一方法:

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

即返回基类从而在子归类OnDeserializeUnrecognizedAttribute()从不被调用。

所以问题是:如何让孩子们通过自己解决未知元素?

回答

2

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

+0

我知道这个答案是前一段时间发布的,但是如果您将此标记为接受的答案,您可以进一步详细了解一下吗? – goric 2012-10-03 21:16:01

+0

@goric:您需要继承它并重写`RetrieveConfigurationElementType`以在运行时替换类型。另请参阅[NameTypeConfigurationElementCollection](http://msdn.microsoft.com/en-us/library/ee939739%28PandP.50%29.aspx) – abatishchev 2012-10-03 22:18:46

1

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

由于CreateNewElement方法没有提供关于元素属性的任何信息,所以不是可能发生特定类型实例化的地方。

经由反射一些挖后,一个涉及到以下的局部调用堆栈:

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铸造项目特定的类型。

11

我也研究过这个。 PolymorphicConfigurationElementCollection<T>seems deprecated。 编辑:它不是,见下面的'abatishchev'的评论,我只是连接一个旧版本。

Rest Wing的解决方案很有前途,但不幸的是需要调用驻留在另一个命名空间中的内部方法。虽然这可以通过反思,但在这种情况下,它不会收到编码美的价格。

我挖入源与反思过,并用以下解决方案上来:

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

相关问题