具有多个不同类型的 ConfigurationElement 的 ConfigurationElementCollection
-
29-10-2019 - |
题
是否可以拥有一个包含许多不同类型的 CollectionElement 的 CollectionElementCollection,例如:
<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
}
IE。因此返回基类 OnDeserializeUnrecognizedAttribute()
在儿童班级中从未被叫过。
所以问题是:如何让子类自己解决未知元素?
解决方案 2
Microsoft.Practices.EnterpriseLibrary.Common.Configuration.PolymorphicConfigurationElementCollection<T>
来自 EntLib5 的这项工作是一种魅力。
其他提示
我也调查了这一点。 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 的基类,您可以在其中定义一些共享属性。
特定类型的实例 (MyType1
和 MyType2
)需要创建以便子类自己解析未知元素或属性。
自从 CreateNewElement
方法不提供有关元素属性的任何信息,这不是可以发生特定类型实例化的地方。
通过 Reflector 进行一些挖掘后,我们得到以下部分调用堆栈:
VariantCollection.MyCollection.CreateNewElement()
System.Configuration.ConfigurationElementCollection.CallCreateNewElement()
System.Configuration.ConfigurationElementCollection.OnDeserializeUnrecognizedElement(string elementName, XmlReader reader)
这 OnDeserializeUnrecognizedElement
方法可以被重写 MyCollection
类以创建特定类型的实例。而不是使用无参数 CallCreateNewElement
方法,使用新的接收 XmlReader
:
- 读取属性
type
(确保其存在和有效性)。 - 创建指定类型的新元素。
- 称呼
internal virtual void AssociateContext( BaseConfigurationRecord configRecord )
的方法System.Configuration.ConfigurationElement
在元素上。 - 称呼
internal void CallInit()
的方法System.Configuration.ConfigurationElement
在元素上。 - 返回准备好的元素。
顺便说一句,如果不会有太多不同的集合元素,请考虑使用类似以下内容的内容:
<myType1Collection>
<add Type1SpecificProp="1" />
</myType1Collection>
<myType2Collection>
<add Type2SpecificProp="2" />
</myType2Collection>
这样你就可以避免从 MyCollection
到特定类型。