.NET自定义配置 - 我可以使用非同类元素进行元素集合吗?

Iva*_*vov 6 c# configuration

我在app/web.config中注册了自定义配置部分,让我们调用它MySection.我ElementCollection在该部分中有一个元素,称为MyElements.在元素集合中,我想拥有由不同类表示的元素 - 这个想法是这些类似的类具有一些常见属性,一些特定于实例.

这是一些xml配置示例:

<MySection>
  <MyElements>
    <Element1 name="someProp1" value="someValue" />
    <Element2 name="someProp2" format="{0}{1}" />
  </MyElements>
</MySection>
Run Code Online (Sandbox Code Playgroud)

在我的简单示例中,所有元素必须具有"name"属性,一些元素还具有"value"属性,另一个元素具有"format"属性.在这里,我希望Element1Element2在.NET运行时中由两个不同的类表示,这两个类具有定义"name"属性的公共基类.

至于我已经深入研究.NET配置,我得到的印象是元素集合(如'MyElements'在这里)应该包含同类元素(只有一种类型).那么,是否有可能实现我想要的东西 - 让它包含不同类的元素.我们的想法是避免为不同的元素类型提供多个元素集合,而不是为每个自定义ConfigurationElement实现编写所有重复属性.

Ole*_*sov 8

您可以通过覆盖ElementCollection类中的OnDeserializeUnrecognizedElement方法并通过为ex创建标记名称来创建Element1和Element2的表示来实现此目的.但AFAIR子元素无论如何应该来自共同的祖先,否则这样做太麻烦了.

将集合定义为:

public class MyElementCollection : ConfigurationElementCollection
{
    const string ELEMENT1 = "Element1";
    const string ELEMENT2 = "Element2";

    protected override ConfigurationElement CreateNewElement ()
    {
        return new MyElement (this);
    }

    protected override object GetElementKey (ConfigurationElement element)
    {
        return ((MyElement)element).Key;
    }

    // This method called when framework sees unknown element tag
    // inside the collection. You can choose to handle it yourself
    // and return true, or return false to invoke default action
    // (exception will be thrown).
    protected override bool OnDeserializeUnrecognizedElement (string elementName, XmlReader reader)
    {
        if (elementName == ELEMENT1 || elementName == ELEMENT2 {
            var myElement = new MyElement (this);

            switch (elementName) {
            case ELEMENT1:
                myElement.Type = MyElementType.Element1;
                break;
            case ELEMENT2:
                myElement.Type = MyElementType.Element2;
                break;
            }

            myElement.DeserializeElementForConfig (reader, false);
            BaseAdd (myElement);

            return true;
        }

        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

和子元素:

public enum MyElementType
{
    Element1,
    Element2,
}

public class MyElement : ConfigurationElement
{
    const string NAME = "name";
    const string VALUE = "value";
    const string FORMAT = "format";

    // keys should be unique, current collection count will do
    // the trick without introducing artificial keys
    public MyElement (ConfigurationElementCollection collection)
    {
        Key = collection.Count;
    }

    // note that this is not ConfigurationProperty
    public int Key { get; private set; }

    // note that this is not ConfigurationProperty
    public MyElementType Type { get; set; }

    [ConfigurationProperty(NAME)]
    public string Name {
        get { return (string)this [NAME]; }
    }

    [ConfigurationProperty(VALUE)]
    public string Value {
        get { return (string)this [VALUE]; }
    }

    [ConfigurationProperty(FORMAT)]
    public string Format {
        get { return (string)this [FORMAT]; }
    }

    // This is called when framework needs a copy of the element,
    // but it knows only about properties tagged with ConfigurationProperty.
    // We override this to copy our Key and Type, otherwise they will
    // have default values.
    protected override void Reset (ConfigurationElement parentElement)
    {
        base.Reset (parentElement);

        var myElement = (MyElement)parentElement;
        Key = myElement.Key;
        Type = myElement.Type;
    }

    // original ConfigurationElement have this protected,
    // redeclaring as protected internal to call it from collection class
    protected internal void DeserializeElementForConfig (XmlReader reader, bool serializeCollectionKey)
    {
        DeserializeElement (reader, serializeCollectionKey);
    }
}
Run Code Online (Sandbox Code Playgroud)