配置文件也可以做成强类型,配置文件的强类型其实就是使配置文件也具有int,DateTime也可以是其他自定义的类型,而不是普通的String类型,至于强类型和一般类型的区别与好处,再次不在叙述,此处只讲解详细的制作步骤。由于使用了强类型,任何时候只要发现配置文件不满足条件就会抛异常,保存时不满足条件的数据也是不能通过系统保存的。当然你可以通过记事本修改在保存,但读取时会抛异常。
主要包括:1单个节点2复合节点,也就是有子节点的节点3配置节4读取配置文件。
以下以一个配置文件为例详细讲解
配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration><configSections><section name="Mail" type="WindowsTestConfig.ClientConfiguration,WindowsTestConfig"/></configSections><Mail LengTh="8"><Services ServicesPort="808" ><Service Address="127.0.0.1" Port="805"/><Service Address="127.0.0.2" Port="804"/><Service Address="127.0.0.3" Port="803" /><!--<Service Type="NMail.MetricsService.MetricsService, NMail.MetricsService" />--></Services> </Mail>
</configuration>
1单个节点 Service可以认为是单个节点,应为里边没有包含子节点,只有属性。
<Service Address="127.0.0.1" Port="805"/>
需要注意的是:
1配置文件的属性名称是区分大小写的,ConfigurationProperty的第一个参数必须要和属性名称相同
2需要继承自基类ConfigurationElement,并根据属性的多少添加不同的方法
3对于每一种常见的类型都有对应的验证方法,例如int是IntegerValidator,字符是StringValidator,还有其他的,功能更强大的正则表达式验证类型RegexStringValidator,但个人试了试不知道RegexStringValidator好像不好用。
4把string转化为其他类型,需要有一个继承自TypeConverter的子类的特性,可以看看 [TypeConverter(typeof(IPAddressTypeConverter))],而继承自TypeConverter也很简单,只需要重写两个方法而已。
ConfigurationElement官网参考
对于此处代码为:
/// <summary>/// A simple class for parsing an IP endpoint out of a configuration file./// </summary>public class IPEndPointElement : ConfigurationElement{/// <summary>/// The endpoint's address./// </summary>[ConfigurationProperty("Address", IsRequired = true)]//Address 需要与配置文件的属性名称相对应,是区分大小写的[TypeConverter(typeof(IPAddressTypeConverter))]//告诉系统,怎么把一个string类型转化为需要的IPAddress 类型,也就是强类型// [StringValidator(InvalidCharacters = " ~!@#$%^&*()[]{}/;'\"|\\",MinLength = 7, MaxLength = 15)]//字符串验证属性// [RegexStringValidator(@"^(\d{1,3}\.){3}\d{1,3}{1}quot;)]//正则表达式验证属性public IPAddress Address{get{return (IPAddress)this["Address"];}set{this["Address"] = value;}}/// <summary>/// The endpoint's port./// </summary>[ConfigurationProperty("Port", IsRequired = true, DefaultValue = 80)]///Port 需要与配置文件的属性名称相对应,是区分大小写的[IntegerValidator(MinValue = 1, MaxValue = 65536)]//int 类型验证,设置区间public int Port{get{return (int)this["Port"];}set{this["Port"] = value;}}/// <summary>/// Converts this instance to an IPEndPoint./// </summary>/// <returns>The endpoint.</returns>public IPEndPoint ToEndPoint()//返回强类型结果{return new IPEndPoint((IPAddress)this["Address"], (int)this["Port"]);}public override string ToString(){return this["Address"] + ":" + this["Port"];}}/// <summary>/// A type converter to convert strings to IP addresses./// </summary>public class IPAddressTypeConverter : TypeConverter//把一个string怎样转化为IPAddress,需要继承TypeConverter,并重写一下的两个方法就可以了{public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType){if (sourceType == typeof(string)){return true;}return base.CanConvertFrom(context, sourceType);}public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value){if (value is string){return IPAddress.Parse((string)value);}return base.ConvertFrom(context, culture, value);}}
2复合节点 Services 可以认为是一个复合节点,因为他包含了一个子节点集合
<Services ServicesPort="808" ><Service address="127.0.0.1" Port="805"/><Service Address="127.0.0.2" Port="804"/><Service Address="127.0.0.3" Port="803" /><!--<Service Type="NMail.MetricsService.MetricsService, NMail.MetricsService" />--></Services>
1配置文件的属性名称是区分大小写的,ConfigurationProperty的第一个参数必须要和属性名称相同
2需要继承自基类ConfigurationElementCollection,并根据属性的多少添加不同的方法
3由于是集合多了几个方法需要重写ConfigurationElementCollection官网参考
对应的代码为
public class IPAddressCollection : ConfigurationElementCollection{protected override ConfigurationElement CreateNewElement()//创建一个写的子节点调用方法{return new IPEndPointElement();}/// <summary>/// The endpoint's port./// </summary>[ConfigurationProperty("ServicesPort", IsRequired = true, DefaultValue = 80)]//需要与配置文件一致[IntegerValidator(MinValue = 1, MaxValue = 65535)]//int 类型的验证public int ServicesPort{get{return (int)this["ServicesPort"];}set{this["ServicesPort"] = value;}}protected override object GetElementKey(ConfigurationElement element)//其他的都是集合相关的操作,只需要很少的改变{IPEndPointElement address = (IPEndPointElement)element;return getKey(address);}/// <summary>/// Gets or sets the IP address element for the given index./// </summary>/// <param name="index">The index of the address to get or set.</param>/// <returns>The address element.</returns>public IPEndPointElement this[int index]//{get{return (IPEndPointElement)BaseGet(index);}set{if (BaseGet(index) != null){BaseRemove(index);}BaseAdd(index, value);}}/// <summary>/// Gets the number of address in this instance./// </summary>public new int Count{get { return base.Count; }}public int IndexOf(IPEndPointElement endPoint){return BaseIndexOf(endPoint);}public void RemoveAt(int index){BaseRemoveAt(index);}public void Add(IPEndPointElement item){BaseAdd(item);}public void Clear(){BaseClear();}public bool Contains(IPEndPointElement item){return BaseIndexOf(item) >= 0;}public void CopyTo(IPEndPointElement[] array, int arrayIndex){base.CopyTo(array, arrayIndex);}public new bool IsReadOnly{get { return false; }}public bool Remove(IPEndPointElement item){if (BaseIndexOf(item) >= 0){BaseRemove(item);return true;}return false;}public IPAddress[] ToAddressArray(){List<IPAddress> addresses = new List<IPAddress>();for (int i = 0; i < this.Count; i++){addresses.Add(((IPEndPointElement)this[i]).Address);}return addresses.ToArray();}/// <summary>/// Gets the key by which address are mapped in the base class./// </summary>/// <param name="endPoint">The address to get the key from.</param>/// <returns>The key.</returns>private string getKey(IPEndPointElement address){return address.Address.ToString();}}
3配置节 Mail可以认为是一个配置节,因为它外边在没有自定义的东西了
<Mail LengTh="8">
</Mail>
1配置文件的属性名称是区分大小写的,ConfigurationProperty的第一个参数必须要和属性名称相同 2需要继承自基类ConfigurationSection,并根据属性的多少添加不同的方法
3根据包含的子元素写一些方法
4由于自己写的配置节,系统并不知道,所以需要在配置文件中注册。也就是在配置文件中添加如下代码, name是这一配置节的名称,type用逗号分成两部分,前一部分是配置节的类名称包含命名空间,后一部分是配置节类所在的程序集名称。
<configSections><section name="Mail" type="WindowsTestConfig.ClientConfiguration,WindowsTestConfig"/></configSections>
ConfigurationSection官网参考 对应的代码为:
class ClientConfiguration : ConfigurationSection {public static readonly string SectionsName = "Mail";//配置节名称public static void AddToConfigFile(){if (ConfigFile.Current.Sections[SectionsName] == null)//添加配置节{ConfigFile.Current.Sections.Add(SectionsName, new ClientConfiguration());}}/// <summary>/// Returns true if a DNS client configuration section exists in the current/// configuration./// </summary>public static bool ConfigurationPresent{get{return (ConfigFile.Current.Sections[SectionsName] != null);}}/// <summary>/// Returns the current DNS client configuration./// </summary>public static ClientConfiguration Current//获取配置节{get{ClientConfiguration current;current = (ClientConfiguration)ConfigFile.Current.GetSection(SectionsName);if (current == null){throw new ConfigurationErrorsException("Mail configuration is missing");}return current;}}/// <summary>/// The list of DNS servers to attempt lookups on./// </summary>[ConfigurationProperty("Services", IsDefaultCollection = false)]//Services需要与配置文件一致[ConfigurationCollection(typeof(IPEndPointElement), AddItemName = "Service",ClearItemsName = "ClearServer", RemoveItemName = "RemoveServer")]//对应集合的几个方法public IPAddressCollection DnsServers{get{return (IPAddressCollection)this["Services"];}}[ConfigurationProperty("LengTh", IsRequired = true)]//一个属性,名称需要与配置文件一致public int LengTh{get{return (int)this["LengTh"];}set{this["LengTh"] = value;}}}
4读取配置文件 自己写的配置节系统并不知道,所以需要进行一定处理,配置节类需要用到这个类
处理代码:
class ConfigFile{/// <summary>/// Opens the configuration files and sets up the shared instance of this config./// </summary>static ConfigFile(){ string path= Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName+".config";//获取配置文件路径包含名称current = OpenConfig(path);}private static Configuration current;public static Configuration Current {//获取当前节get {return current;}set {current = value;}}public static Configuration OpenConfig(string configFilename) {ExeConfigurationFileMap cfm = new ExeConfigurationFileMap();cfm.ExeConfigFilename = configFilename;return ConfigurationManager.OpenMappedExeConfiguration(cfm, ConfigurationUserLevel.None);}}
测试:
private void ReadBtn_Click(object sender, EventArgs e)//读取配置文件,如果配置文件不满足条件,读取时就会抛异常{Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);ClientConfiguration data = config.Sections[ClientConfiguration.SectionsName] as ClientConfiguration;if (data == null){return;}LengThBox.Text= data.LengTh+"";ServicesPort.Text = data.DnsServers.ServicesPort + "";if(data.DnsServers.Count>=1){Service1IPBox.Text = data.DnsServers[0].Address.ToString();Service1PortBox.Text = data.DnsServers[0].Port +"";Service2IPBox.Text = data.DnsServers[1].Address.ToString();Service2PortBox.Text = data.DnsServers[1].Port + "";Service3IPBox.Text = data.DnsServers[2].Address.ToString();Service3PortBox.Text = data.DnsServers[2].Port + "";}}private void SaveBtn_Click(object sender, EventArgs e)//修改配置文件,如果配置文件不满足条件,写入时时就会抛异常{Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);ClientConfiguration data = config.Sections[ClientConfiguration.SectionsName] as ClientConfiguration;if (data == null){return;}data.LengTh=int.Parse(LengThBox.Text);data.DnsServers.ServicesPort = int.Parse(ServicesPort.Text);if (data.DnsServers.Count >= 1){data.DnsServers[0].Address=IPAddress.Parse(Service1IPBox.Text);data.DnsServers[0].Port = int.Parse(Service1PortBox.Text); data.DnsServers[1].Address = IPAddress.Parse(Service2IPBox.Text);data.DnsServers[1].Port = int.Parse(Service2PortBox.Text); data.DnsServers[2].Address = IPAddress.Parse(Service3IPBox.Text);data.DnsServers[2].Port = int.Parse(Service3PortBox.Text); }config.Save(ConfigurationSaveMode.Minimal);}}
源代码下载