App.config的学习笔记
昨天基本弄清config的使用之后,再看WP的API,晕了。结果WP不支持system.configuration命名空间,这意味着想在WP上用App.config不大可能了。
WP具体支持API请查看
不过还是记录下App.config的使用。
有很大部分是从MSDN学来的,如果有人看我的这篇文章的话可以先去看看MSDN的相关章节 http://msdn.microsoft.com/en-us/library/system.configuration.configuration(v=vs.110).aspx
一.appSettings
这个比较简单,也有很多资料讲到,在我的C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config中,有machine.config这个文件
有下面节选
1 <configuration> 2 <configSections> 3 <section name="appSettings" type="System.Configuration.AppSettingsSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" restartOnExternalChanges="false" requirePermission="false"/> 4 <section name="connectionStrings" type="System.Configuration.ConnectionStringsSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" requirePermission="false"/>
后面长串省略………………
所以appSettings是一个section,当在配置文件中输入 <appSettings>节点时,系统会帮我们映射到System.Configuration.AppSettingsSection这个类中去,用reflector看看这个类
继承自ConfigurationSection
1 private static volatile ConfigurationProperty s_propAppSettings; 2 private static volatile ConfigurationProperty s_propFile;
其跟App.config中映射的字段
1 ConfigurationProperty property = new ConfigurationProperty(null, typeof(KeyValueConfigurationCollection), null, ConfigurationPropertyOptions.IsDefaultCollection); 2 ConfigurationProperty property2 = new ConfigurationProperty("file", typeof(string), string.Empty, ConfigurationPropertyOptions.None);
1 s_propAppSettings = property;
2 s_propFile = property2;
属性暴露出来为:
file(attribute)
不作为元素:
1 [ConfigurationProperty("file", DefaultValue="")] 2 public string File 3 { 4 get 5 { 6 string str = (string) base[s_propFile]; 7 if (str == null) 8 { 9 return string.Empty; 10 } 11 return str; 12 } 13 set 14 { 15 base[s_propFile] = value; 16 } 17 }
element:
1 [ConfigurationProperty("", IsDefaultCollection=true)] 2 public KeyValueConfigurationCollection Settings 3 { 4 get 5 { 6 return (KeyValueConfigurationCollection) base[s_propAppSettings]; 7 } 8 }
对于不是嵌套的element,而是直接作为section的子节点集合,就照这样写,这样写没有在App.config的对应名字,看起来全是add元素(当然也可以有,后面会说明)
再来看看KeyValueConfigurationCollection这个集合继承自ConfigurationElementCollection
其中重要的两个方法:
1 protected override ConfigurationElement CreateNewElement() 2 { 3 return new KeyValueConfigurationElement(); 4 } 5 6 protected override object GetElementKey(ConfigurationElement element) 7 { 8 return ((KeyValueConfigurationElement) element).Key; 9 }
所以其实在每个Add中,Add的是KeyValueConfigurationElement,这个类的Key属性作为集合的关键字,再来看看KeyValueConfigurationElement类,这个类继承自ConfigurationElement
关键部分:
1 private static readonly ConfigurationProperty _propKey = new ConfigurationProperty("key", typeof(string), string.Empty, ConfigurationPropertyOptions.IsKey | Configurat ionPropertyOptions.IsRequired); 2 private static readonly ConfigurationProperty _propValue = new ConfigurationProperty("value", typeof(string), string.Empty, ConfigurationPropertyOptions.None);
3 [ConfigurationProperty("key", Options=ConfigurationPropertyOptions.IsKey, DefaultValue="")] 4 public string Key 5 { 6 get 7 { 8 return (string) base[_propKey]; 9 } 10 } 11 [ConfigurationProperty("value", DefaultValue="")] 12 public string Value 13 { 14 get 15 { 16 return (string) base[_propValue]; 17 } 18 set 19 { 20 base[_propValue] = value; 21 } 22 }
具体到element的key,value了。这样我们也可以自定义节点了。
二.模仿appSettings做自己的setting
创建一个控制台应用和一个类库,在控制台中先添加system.configuration程序集的引用。然后我们写上自己想要的App.config的内容。如下
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 8 <Songs ChannelId="1"> 9 <add name="1.mp3" length="100" /> 10 <add name="2.mp3" length="100" /> 11 <add name="3.mp3"/> 12 <add length="200"/> 13 </Songs> 14 15 </configuration>
这样算是定义好了一些配置,其中有些song配置使用了默认的值。
但是为了使Songs这个Section工作起来,我们也需要像Appsettings一样添加自定义Section,我们添加 <configSections>元素,再在里面添加Section,添加好,完整如下
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <configSections> 7 <section name="Songs" type="ConfigLib.SongsSection,ConfigLib"/> 8 </configSections> 9 10 <Songs ChannelId="1"> 11 <add name="1.mp3" length="100" /> 12 <add name="2.mp3" length="100" /> 13 <add name="3.mp3"/> 14 <addlength="200"/> 15 </Songs> 16 17 </configuration>
其中ConfigLib为我们一开始添加的库的名字 ConfigLib.SongsSection为要映射到的类。
接下看看SongsSection类。
同样在ConfigLib这个Lib中添加system.configuration这个dll,然后使SongsSection这个类去继承ConfigurationSection类
1 namespace ConfigLib 2 { 3 public class SongsSection:ConfigurationSection 4 { 5 } 6 }
由于我们的appSettings的子节点都是数据的单个点,总共是一个集合,所以我们还要一个集合类用来存放所有的song.这个集合要继承ConfigurationElementCollection类
1 namespace ConfigLib 2 { 3 class Songcollection:ConfigurationElementCollection 4 { 5 } 6 }
但是这样是编译不过的,因为ConfigurationElementCollection类里有2个标注为abstract的方法需要我们子类来实现,先让它编译通过,如下
1 public class Songcollection:ConfigurationElementCollection 2 { 3 protected override ConfigurationElement CreateNewElement() 4 { 5 throw new NotImplementedException(); 6 } 7 protected override object GetElementKey(ConfigurationElement element) 8 { 9 throw new NotImplementedException(); 10 } 11 }
先不管实现,我们用这个Collection来装song.回到SongsSection类,我们可以使用上面的集合了,像appSettings那样,但要注意,我们为Songs这个Section添加了类似XML中属性(attribute)的channleid,由于这个不是Element,是个int类型(后面还有ConfigurationElement类),所以我们在类中属性(Property)中使用的时候这个的时候不会跑到element中去,它会乖乖在XML的属性位置。代码如下
1 namespace ConfigLib 2 { 3 public class SongsSection:ConfigurationSection 4 { 5 private readonly ConfigurationProperty collectionproperty = new ConfigurationProperty(null, typeof(Songcollection), null, ConfigurationPropertyOptions.IsDefaultCollection); 6 [ConfigurationProperty("",IsDefaultCollection=true)] 7 public Songcollection SongCollection 8 { 9 get 10 { 11 return (Songcollection)this[collectionproperty]; 12 13 } 14 } 15 [ConfigurationProperty("ChannelId", IsKey = false, DefaultValue = "-1")] 16 public int ChannelId 17 { 18 get 19 { 20 return (int)this["ChannelId"]; 21 } 22 } 23 } 24 }
其中SongCollection属性的写法很像appSettings的写法。
接下来看看Songcollection这个集合类最简单的写法:
1 namespace ConfigLib 2 { 3 public class Songcollection:ConfigurationElementCollection 4 { 5 protected override ConfigurationElement CreateNewElement() 6 { 7 return new SongElement(); 8 } 9 protected override object GetElementKey(ConfigurationElement element) 10 { 11 return ((SongElement)element).Name; 12 } 13 14 } 15 }
集合类的很多属性我们使用父类的,字面意思都比较好理解,其中GetElementKey是为这个集合指定关键字的,有点像数据库中的Key,这个Key不能重复,并且我们可以通过
这个Key获得集合中的元素(SongElement).
集合中主要是Element,Element基本就和XML中Element一样,SongElement这个类如下,用这个类(SongElement)定义的变量就是(app.config)XML中的Element,而不是Attribute,(类中的所有简单类型都在Attribute中出现)
1 namespace ConfigLib 2 { 3 public class SongElement:ConfigurationElement 4 { 5 [ConfigurationProperty("name",IsKey=true,DefaultValue="default.mp3")] 6 public string Name 7 { 8 get 9 { 10 return (string)this["name"]; 11 } 12 } 13 [ConfigurationProperty("length", IsKey = false, DefaultValue = "-1")] 14 public int Length 15 { 16 get 17 { 18 return (int)this["length"]; 19 } 20 } 21 } 22 }
为每个XML的attribute设置默认值,并且不为必须的,写App.config的时候保证Key(这里是歌的名字)不重复,如果需要通过索引访问song的集合,在集合中加如下索引器:
1 public SongElement this[int index] 2 { 3 get 4 { 5 return (SongElement)BaseGet(index); 6 } 7 }
由于集合是默认的AddClear类型,所以我们的XML元素(element)虽然存在,但是要写成add,clear形式,(如果有clear,clear写在哪,那么之前add过的元素就都不见了)
App.config还可以改成下面这样
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <configSections> 4 <section name="Songs" type="ConfigLib.SongsSection,ConfigLib"/> 5 </configSections> 6 <Songs ChannelId="1"> 7 <song name="1.mp3" length="100" /> 8 <song name="2.mp3" length="100" /> 9 <song name="3.mp3"/> 10 <song length="200"/> 11 </Songs> 12 </configuration>
把add改成了song,这样顺眼一点
那仅仅只要在集合类中添加:
1 protected override string ElementName 2 { 3 get 4 { 5 return "song"; 6 } 7 } 8 public override ConfigurationElementCollectionType CollectionType 9 { 10 get 11 { 12 return ConfigurationElementCollectionType.BasicMap; 13 } 14 }
就可以了
如果App.config改为下面这样:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <configSections> 4 <section name="Songs" type="ConfigLib.SongsSection,ConfigLib"/> 5 </configSections> 6 <Songs ChannelId="1"> 7 <special_special_song specialsongname="special_1.mp3"/> 8 <specialsongs> 9 <specialsong specialsongname="special_2.mp3"/> 10 <specialsong specialsongname="special_3.mp3"/> 11 <specialsong specialsongname="special_4.mp3"/> 12 </specialsongs> 13 <song name="1.mp3" length="100" /> 14 <song name="2.mp3" length="100" /> 15 <song name="3.mp3"/> 16 <song length="200"/> 17 </Songs> 18 </configuration>
一样的道理,加一个SpecialSongElement类和容纳这个类的集合SpecialSongCollection,然后再在Section中做添加即可,在section类中,第一个
<special_special_song specialsongname="special_1.mp3"/>对应:
1 [ConfigurationProperty("special_special_song", IsKey = false)] 2 public SpecialSongElement SpecialSong 3 { 4 get 5 { 6 return (SpecialSongElement)this["special_special_song"]; 7 } 8 }
而集合
<specialsongs>对应:
1 [ConfigurationProperty("specialsongs", IsKey = false, IsDefaultCollection = false)] 2 public SpecialSongCollection SpecialSongs 3 { 4 get 5 { 6 return (SpecialSongCollection)this["specialsongs"]; 7 } 8 }
注意IsDefaultCollection = false。这个SpecialSongCollection集合跟前面的集合基本一样。
最后的效果:
如果有多个Section的时候可以考虑使用sectionGroup来给各个Section分组
像系统的C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config一样
还有虽然微软不提倡使用IConfigurationSectionHandler来解析Section,但是我觉得这个方法知道也不错,这个解析方法是把getsection中的section作为根XmlNode,传到接口实现的方法 public object Create(object parent, object configContext, System.Xml.XmlNode section)中
其中System.Xml.XmlNode section参数就是根xmlNode,然后就可以操作Xml了。