具有多个不同类型的 ConfigurationElement 的 ConfigurationElementCollection

发布于 2024-10-15 23:24:27 字数 1019 浏览 7 评论 0原文

是否可以拥有一个具有许多不同类型 CollectionElements 的 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 有 next 方法:

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

即返回基类,因此 <子类中的 code>OnDeserializeUnrecognizedAttribute() 从未被调用。

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

Is it possible to have a CollectionElementCollection with a number of different by type CollectionElements, e.g.:

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

I have all classes required for such solution:

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

but when I start my application I'm getting next predictable error:

Unrecognized attribute 'Type1SpecificProp'.

because Type1SpecificProp is defined in MyType1 not MyElement, especially if MyCollection has next method:

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

i.e. returns base class thus OnDeserializeUnrecognizedAttribute() in child classed are never been called.

So the question is: how to let child classes to resolve unknown elements by their self?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一影成城 2024-10-22 23:24:27

我也调查了这一点。 PolymorphicConfigurationElementCollection 似乎已弃用
编辑:不是,请参阅下面“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 的基类,您可以在其中定义一些共享属性。

I looked into this as well. PolymorphicConfigurationElementCollection<T> seems deprecated.
Edit: it's not, see the comment of 'abatishchev' below, i was just linking an old version.

The solution of Rest Wing was promising but unfortunately required invoking internal methods residing in another namespace. While this is possible via reflection it isn't going to receive prices for coding beauty in this case.

I digged into the source with Reflection too and came up with the following solution:

[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; }
    }
}

The override of the CollectionType is REQUIRED, even if this has been specified via the attribute in the top. When not overridden the base class' CollectionType still refers to 'AddRemoveClearMap' which isn't going to trigger the required 'CreateNewElement(string elementName)' function but it's parameterless variant 'CreateNemElement()'. For the same reason the overwritten IsElementName function should return true.

Note that I created a ElementBaseConfig which is the base class of both MyType1Config and MyType2Config in which you could define some shared attributes.

与往事干杯 2024-10-22 23:24:27

来自 EntLib5 的 Microsoft.Practices.EnterpriseLibrary.Common.Configuration.PolymorphicConfigurationElementCollection 可以作为一个魅力来完成这项工作。

Microsoft.Practices.EnterpriseLibrary.Common.Configuration.PolymorphicConfigurationElementCollection<T> from EntLib5 do this job as a charm.

记忆之渊 2024-10-22 23:24:27

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

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

通过 Reflector 进行一些挖掘后,我们得到以下部分调用堆栈:

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

可以在 MyCollection 类中重写 OnDeserializeUnrecognizedElement 方法,以便创建特定类型实例。不要使用无参数 CallCreateNewElement 方法,而是使用接收 XmlReader:

  1. Read attribute type 的新方法(确保其存在和有效性)。
  2. 创建指定类型的新元素。
  3. 在元素上调用 System.Configuration.ConfigurationElement内部 virtual void AssociateContext( BaseConfigurationRecord configRecord ) 方法。
  4. 在元素上调用 System.Configuration.ConfigurationElementinternal void CallInit() 方法。
  5. 返回准备好的元素。

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

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

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

An instance of specific type (MyType1 and MyType2) needs to be created in order for child classes to resolve unknown elements or attributes by themselves.

Since the CreateNewElement method does not give any information on an element's attributes, that is not place where specific type instantiation can occur.

After some digging via Reflector, one comes to following partial call stack:

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

The OnDeserializeUnrecognizedElement method can be overriden in MyCollection class in order to create specific type instance. Instead of using the parameterless CallCreateNewElement method, use new one that receives XmlReader:

  1. Read attribute type (ensure its existence and validity).
  2. Create new element of specified type.
  3. Call internal virtual void AssociateContext( BaseConfigurationRecord configRecord ) method of System.Configuration.ConfigurationElement on the element.
  4. Call internal void CallInit() method of System.Configuration.ConfigurationElement on the element.
  5. Return the prepared element.

BTW, if there will not be too many different collection elements, consider using something like:

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

That way you can avoid casting the items from MyCollection to specific type.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文