问题
Lets say I have the following configuration section....
<yak>
<whaa hello="world" />
</yak>
<yak>
<whaa hello="world" blot="1" />
</yak>
Because the second <whaa>
element has the extra attribute on it I want to map it to a sub type of the the type that's mapped to the first <whaa>
element.
So how do I get polymorphic binding?
回答1:
Here's one approach by using a programmatic solution which overrides the OnDeserializeUnrecognizedElement
method of the ConfigurationSection
class. The base and child configuration element classes:
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;
}
}
}
The configuration section class:
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;
}
}
The sample usage:
var yakSectionSettings = (Yak)ConfigurationManager.GetSection("yak");
And the configuration markup:
<configSections>
<section name="yak" type="Yak, MyApplication"/>
</configSections>
You may use now either:
<yak>
<whaa hello="world"/>
</yak>
for getting a Whaa
object, or:
<yak>
<whaa hello="world" blot="1"/>
</yak>
for getting a WhaaChild
object at runtime.
回答2:
This solution uses a Proxy class to wrap the subtype implementations. There is a comment at the bottom that even refers to naming the collection elements differently for the different subtypes, so along the lines of:
<yak>
<whaa hello="world" />
</yak>
<yak>
<whaaChild hello="world" blot="1" />
</yak>
Posting the excerpts below just in case the link dies someday, but please support their blog by trying the link first!
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);
}
}
}
}
来源:https://stackoverflow.com/questions/20882000/polymorphic-custom-configuration-section