I started by reading through this blog, which was very informative, but didn't totally do what I needed it to do. I extended that example to meet my needs, and this is what I ended up with:
1: <configSections>
2: <section name="AssemblySettings" type="UnitTestCoverage.ConfigurationExtensions.AssemblySettings, UnitTestCoverage.ConfigurationExtensions"/>
3: <section name="FilterSettings" type="UnitTestCoverage.ConfigurationExtensions.FilterSettings, UnitTestCoverage.ConfigurationExtensions"/>
4: </configSections>
5: <appSettings>...snipped...</appSettings>
6: <AssemblySettings>
7: <Assemblies>
8: <clear />
9: <add key="SomeProject.Test" value="C:\Dev\SomeProject.Test\bin\Debug\SomeProject.Test.dll" outputDirectory="C:\Output\SomeProject.Test\" outputFile="SomeProject.Test.xml">
10: <filters>
11: <clear />
12: <add key="FirstName" value="[FirstNamespace]SomeProject.FirstNamespace.*" type="Include" />
13: <add key="SecondName" value="[SecondNamespace]SomeProject.SecondNamespace.*" type="Include" />
14: <add key="Test" value="[SomeProject.Test]" type="Ignore" />
15: </filters>
16: </add>
17: <add key="SomeOtherProject.Test" value="C:\Dev\SomeOtherProject.Test\bin\Debug\SomeOtherProject.Test.dll" outputDirectory="C:\Output\SomeOtherProject.Test\" outputFile="SomeOtherProject.Test.xml">
18: <filters>
19: <clear />
20: <add key="SomeOtherProject" value="[SomeOtherNamespace]*" type="Include" />
21: <add key="SomeOtherProjectTest" value="[SomeOtherNamespace.Test]*" type="Ignore" />
22: </filters>
23: </add>
24: </Assemblies>
25: </AssemblySettings>
So how did I do that? I'd love to show you. To get what I have above, I had to create five new objects in my solution. I decided to keep this part separate from my business logic by creating a ConfigurationExtensions project to hold everything. You don't have to do that, of course, but I found it to be easiest. The first class I created was the AssemblySettings class, which inherits from System.Configuration.ConfigurationSection and corresponds to
<AssemblySettings>
up there.
1: public class AssemblySettings : ConfigurationSection
2: {
3: [ConfigurationProperty("Assemblies", IsDefaultCollection = false)]
4: [ConfigurationCollection(typeof(AssemblyCollection), AddItemName = "add", RemoveItemName = "remove",
5: ClearItemsName = "clear")]
6: public AssemblyCollection Assemblies
7: {
8: get
9: {
10: AssemblyCollection assemblyCollection = (AssemblyCollection)base["Assemblies"];
11: return assemblyCollection;
12: }
13: }
14: }
The next class is AssemblyCollection (which you may have noticed is referenced on line 4 of AssemblySettings). This class inherits from ConfigurationElementCollection and it doesn't have a direct partner in the web.config. However, this is the class that allows me to have multiple Assembly items in the web.config so it is very important.
1: public class AssemblyCollection : ConfigurationElementCollection
2: {
3: public override ConfigurationElementCollectionType CollectionType
4: {
5: get { return ConfigurationElementCollectionType.AddRemoveClearMap; }
6: }
7:
8: protected override ConfigurationElement CreateNewElement()
9: {
10: return new AssemblyElement();
11: }
12:
13: protected override object GetElementKey(ConfigurationElement element)
14: {
15: return ((AssemblyElement)element).Key;
16: }
17:
18: public AssemblyElement this[int index]
19: {
20: get { return (AssemblyElement)BaseGet(index); }
21: set
22: {
23: if (BaseGet(index) != null)
24: {
25: BaseRemoveAt(index);
26: }
27: BaseAdd(index, value);
28: }
29: }
30:
31: new public AssemblyElement this[string Name]
32: {
33: get { return (AssemblyElement)BaseGet(Name); }
34: }
35:
36: public int IndexOf(AssemblyElement assembly)
37: {
38: return BaseIndexOf(assembly);
39: }
40:
41: public void Add(AssemblyElement assembly)
42: {
43: BaseAdd(assembly);
44: }
45:
46: protected override void BaseAdd(ConfigurationElement element)
47: {
48: BaseAdd(element, false);
49: }
50:
51: public void Remove(AssemblyElement assembly)
52: {
53: if (BaseIndexOf(assembly) >= 0)
54: {
55: BaseRemove(assembly.Key);
56: }
57: }
58:
59: public void RemoveAt(int index)
60: {
61: BaseRemoveAt(index);
62: }
63:
64: public void Remove(string name)
65: {
66: BaseRemove(name);
67: }
68:
69: public void Clear()
70: {
71: BaseClear();
72: }
73: }
The last Assembly related class is AssemblyElement, which inherits from ConfigurationElement.
1: public class AssemblyElement : ConfigurationElement
2: {
3: public AssemblyElement(string key, string value, string outputDirectory, string outputFile)
4: {
5: Key = key;
6: Value = value;
7: OutputDirectory = outputDirectory;
8: OutputFile = outputFile;
9: }
10:
11: public AssemblyElement()
12: : this("", @"SomeValue.dll", @"C:\Output\", "results.xml")
13: {
14: }
15:
16: [ConfigurationProperty("key", DefaultValue = "", IsRequired = true, IsKey = true)]
17: public string Key
18: {
19: get { return (string)this["key"]; }
20: set { this["key"] = value; }
21: }
22:
23: [ConfigurationProperty("value", DefaultValue = "", IsRequired = true)]
24: public string Value
25: {
26: get { return (string)this["value"]; }
27: set { this["value"] = value; }
28: }
29:
30: [ConfigurationProperty("noshadow", DefaultValue = "true", IsRequired = false)]
31: public bool NoShadow
32: {
33: get { return (bool) this["noshadow"]; }
34: set { this["noshadow"] = value; }
35: }
36:
37: [ConfigurationProperty("outputFile", DefaultValue = @"results.xml", IsRequired = true)]
38: public string OutputFile
39: {
40: get { return (string)this["outputFile"]; }
41: set { this["outputFile"] = value; }
42: }
43:
44: [ConfigurationProperty("outputDirectory", DefaultValue = @"C:\Output\", IsRequired = true)]
45: public string OutputDirectory
46: {
47: get { return (string)this["outputDirectory"]; }
48: set { this["outputDirectory"] = value; }
49: }
50:
51: [ConfigurationProperty("filters")]
52: public FilterCollection Filters
53: {
54: get { return (FilterCollection)this["filters"]; }
55: set { this["filters"] = value; }
56: }
57: }
You can see in the config file (and in AssemblyElement) that we have a nested configuration for filters, that uses a new class called FilterCollection. FilterCollection inherits from ConfigurationElementCollection.
1: public class FilterCollection : ConfigurationElementCollection
2: {
3: public override ConfigurationElementCollectionType CollectionType
4: {
5: get { return ConfigurationElementCollectionType.AddRemoveClearMap; }
6: }
7:
8: protected override ConfigurationElement CreateNewElement()
9: {
10: return new FilterElement();
11: }
12:
13: protected override object GetElementKey(ConfigurationElement element)
14: {
15: return ((FilterElement)element).Key;
16: }
17:
18: public FilterElement this[int index]
19: {
20: get { return (FilterElement)BaseGet(index); }
21: set
22: {
23: if (BaseGet(index) != null)
24: {
25: BaseRemoveAt(index);
26: }
27: BaseAdd(index, value);
28: }
29: }
30:
31: new public FilterElement this[string Name]
32: {
33: get { return (FilterElement)BaseGet(Name); }
34: }
35:
36: public int IndexOf(FilterElement filter)
37: {
38: return BaseIndexOf(filter);
39: }
40:
41: public void Add(FilterElement filter)
42: {
43: BaseAdd(filter);
44: }
45:
46: protected override void BaseAdd(ConfigurationElement element)
47: {
48: BaseAdd(element, false);
49: }
50:
51: public void Remove(FilterElement filter)
52: {
53: if (BaseIndexOf(filter) >= 0)
54: {
55: BaseRemove(filter.Key);
56: }
57: }
58:
59: public void RemoveAt(int index)
60: {
61: BaseRemoveAt(index);
62: }
63:
64: public void Remove(string name)
65: {
66: BaseRemove(name);
67: }
68:
69: public void Clear()
70: {
71: BaseClear();
72: }
73: }
Since a FilterCollection is made up of individual filters, we'll have to create those as well. The FilterElement class inherits from ConfigurationElement.
1: public class FilterElement : ConfigurationElement
2: {
3: public FilterElement(string key, string type, string value)
4: {
5: Type = type;
6: Value = value;
7: Key = key;
8: }
9:
10: public FilterElement()
11: : this("Namespace", "Add", "[*]*")
12: {
13: }
14:
15: [ConfigurationProperty("key", DefaultValue = "", IsRequired = true, IsKey = true)]
16: public string Key
17: {
18: get { return (string)this["key"]; }
19: set { this["key"] = value; }
20: }
21:
22: [ConfigurationProperty("type", DefaultValue = "Add", IsRequired = true)]
23: public string Type
24: {
25: get { return (string)this["type"]; }
26: set { this["type"] = value; }
27: }
28:
29: [ConfigurationProperty("value", DefaultValue = "[*]*")]
30: public string Value
31: {
32: get { return (string)this["value"]; }
33: set { this["value"] = value; }
34: }
35: }
No comments:
Post a Comment