多态自定义配置部分

时间:2014-01-02 11:32:40

标签: .net custom-configuration

假设我有以下配置部分....

<yak>
    <whaa hello="world" />
</yak>
<yak>
   <whaa hello="world" blot="1" />
</yak>

因为第二个<whaa>元素上有额外的属性,我想将它映射到映射到第一个<whaa>元素的类型的子类型。

那么我如何获得多态绑定?

2 个答案:

答案 0 :(得分:2)

这是使用编程解决方案的一种方法,该解决方案会覆盖OnDeserializeUnrecognizedElement类的ConfigurationSection方法。基本和子配置元素类:

public class Whaa : ConfigurationElement
{
    [ConfigurationProperty("hello", IsRequired = true)]
    public string Hello
    {
        get
        {
            return base["hello"] as string;
        }
        set
        {
            base["hello"] = value;
        }
    }
}

public class WhaaChild : Whaa
{
    [ConfigurationProperty("blot", IsRequired = true)]
    public string Blot
    {
        get
        {
            return base["blot"] as string;
        }
        set
        {
            base["blot"] = value;
        }
    }
}

配置部分类:

public class Yak : ConfigurationSection
{
    public Whaa MyProperty;

    protected override bool
        OnDeserializeUnrecognizedElement(string elementName, XmlReader reader)
    {
        if (elementName == "whaa")
        {
            try
            {
                var hello = reader.GetAttribute("hello");
                if (hello != null)
                {
                    var blot = reader.GetAttribute("blot");
                    if (blot != null)
                    {
                        MyProperty = new WhaaChild()
                        {
                            Blot = blot,
                            Hello = hello
                        };
                    }
                    else
                    {
                        MyProperty = new Whaa()
                        {
                            Hello = hello
                        };
                    }
                }
            }
            catch (Exception)
            {
                // TODO: add exception handling
            }
        }
        return true;
    }
}

样本用法:

var yakSectionSettings = (Yak)ConfigurationManager.GetSection("yak");

配置标记:

<configSections>
    <section name="yak" type="Yak, MyApplication"/>
</configSections>

您现在可以使用:

<yak>
    <whaa hello="world"/>
</yak>

获取Whaa个对象,或者:

<yak>
    <whaa hello="world" blot="1"/>
</yak>

用于在运行时获取WhaaChild对象。

答案 1 :(得分:0)

This解决方案使用Proxy类包装子类型的实现。底部有一条评论,甚至提到针对不同的子类型以不同的方式命名集合元素,因此遵循以下原则:

<yak>
    <whaa hello="world" />
</yak>
<yak>
   <whaaChild hello="world" blot="1" />
</yak>

在下面摘录以防万一链接消失,但是请先尝试链接以支持他们的博客!

NestedConfiguration.cs

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Text;
using System.Xml;

namespace NestedConfiguration
{
  public class CollectionSection : ConfigurationSection
  {
    [ConfigurationProperty("collection", IsDefaultCollection = false)]
    [ConfigurationCollection(typeof(CollectionConfig), AddItemName = "add")]
    public CollectionConfig Collection
    {
      get
      {
        return (CollectionConfig) this["collection"];
      }

      set
      {
        this["collection"] = value;
      }
    }
  }

  public class Parent : ConfigurationElement
  {
    [ConfigurationProperty("name", IsRequired = true)]
    public string Name
    {
      get
      {
        return (string) this["name"];
      }

      set
      {
        this["name"] = value;
      }
    }

    [ConfigurationProperty("type", IsRequired = true)]
    public string Type
    {
      get
      {
        return (string) this["type"];
      }

      set
      {
        this["type"] = value;
      }
    }

    public void ProxyDeserializeElement(XmlReader reader, bool serializeCollectionKey)
    {
      DeserializeElement(reader, serializeCollectionKey);
    }
  }

  public class One : Parent
  {
    [ConfigurationProperty("p1")]
    public string P1
    {
      get
      {
        return (string)this["p1"];
      }

      set
      {
        this["p1"] = value;
      }
    }
  }

  public class Two : Parent
  {
    [ConfigurationProperty("p2")]
    public string P2
    {
      get
      {
        return (string)this["p2"];
      }

      set
      {
        this["p2"] = value;
      }
    }
  }

  public class Proxy : ConfigurationElement
  {
    private Parent _Parent = null;

    public Parent Parent
    {
      get
      {
        return _Parent;
      }
    }

    public Proxy()
    {
    }

    public Parent Instance
    {
      get
      {
        return _Parent;
      }
    }

    protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey)
    {
      string type = reader.GetAttribute("type");
      switch (type)
      {
        case "one":
          _Parent = new One();
          break;
        case "two":
          _Parent = new Two();
          break;
        default:
          throw new ArgumentException(string.Format("Invalid type: {0}", type));
      }

      _Parent.ProxyDeserializeElement(reader, serializeCollectionKey);
    }
  }

  public class CollectionConfig : ConfigurationElementCollection
  {
    public CollectionConfig()
    {

    }

    protected override ConfigurationElement CreateNewElement()
    {
      return new Proxy();
    }

    protected override Object GetElementKey(ConfigurationElement element)
    {
      return ((Proxy)element).Parent.Name;
    }

    public Parent this[int index]
    {
      get
      {
        return ((Proxy)BaseGet(index)).Parent;
      }

      set
      {
        if (BaseGet(index) != null)
        {
          BaseRemoveAt(index);
        }

        BaseAdd(index, value);
      }
    }
  }
}

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <configSections>
  <section name="CollectionSection" type="NestedConfiguration.CollectionSection, NestedConfiguration" />
 </configSections>
 <CollectionSection>
  <collection>
   <add type="one" name="one-1" p1="one-1 p1" />
   <add type="one" name="one-2" p1="one-2 p1" />
   <add type="two" name="two-1" p2="two-1 p2" />
  </collection>
 </CollectionSection>
</configuration>

Program.cs

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Text;

namespace NestedConfiguration
{
  class Program
  {
    static void Main(string[] args)
    {
      try
      {
        ExeConfigurationFileMap map = new ExeConfigurationFileMap();
        map.ExeConfigFilename = "NestedConfiguration.exe.config";
        Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
        CollectionSection config = (CollectionSection)configuration.Sections[typeof(CollectionSection).Name];
        Console.WriteLine("Nested configurations: {0}", config.Collection.Count);
        foreach (Proxy proxy in config.Collection)
        {
          Console.WriteLine("Type: {0}", proxy.Parent.GetType());
        }
      }
      catch(Exception ex)
      {
        Console.WriteLine(ex.Message);
      }
    }
  }
}