• 掌握 .NET 1.1 的配置文件用法


    在 .NET 1.1 中,我们都知道可以使用 app.config 或者 web.config (ASP.NET) 来保存一些设置。可是对于大多数人来说,可能用的最多的只是把它当作一个简单的 ini 文件来存储 key-value 键值对,比如数据库链接字符串,上传文件路径之类的。但是实际上配置文件里可以存放任意复杂的结构。如果读过 DNN,.Text 之类程序的代码,就可以找到这些应用的范例。不过这些项目的代码一般都比较繁杂,因此这里我结合 .Text 的配置方法,对配置文件的用法来做一个简单的小结。 

    一、最简单的写法,只用到 appSettings 元素。

    appSettings 里的设定在 ConfigurationSettings 类里有默认的属性来访问,他返回的是一个 NameValueCollection 子类的实例。所以通常简单的字符串值可以保存在这里。写法如下:

    <? xml version="1.0" encoding="utf-8"  ?>
    < configuration >
        
    <!--  最简单的,在 appSettings 里面写  -->
        
    < appSettings >
            
    <!--  定义两个键值  -->
            
    < add  key ="key1"  value ="123"   />
            
    < add  key ="key2"  value ="456"   />
        
    </ appSettings >
    </ configuration >


    读取的代码:

    string key1 = ConfigurationSettings.AppSettings["key1"];
    string key2 = ConfigurationSettings.AppSettings["key2"];


    二、稍微加点料。。

    appSettings 中不仅仅可以用 add 来添加键值,还可以用 clear 或 remove 元素。

    clear 的意思是,去除父层次的配置文件中定义的所有键值。

    所谓“父层次”的意思是,比如我们在 ASP.NET 中,当我们用 ConfigurationSettings.AppSettings[key] 去读取一个值的时候,首先会去检查 machine.config 里是否有此键值的配置,然后再去读取 web.config. 另外,如果在不同的目录层次中配置 web.config,则子目录中 web.config 的配置会覆盖父目录中的设置。那么这里 machine.config 相对于当前的 web.config, 或者父目录的 config 文件相对于子目录的 config 文件,就是一个父子层次的关系。

    remove 则可以移除一个父层次中设定的键值。

    加入这两种语法后的配置文件如下:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        
    <!-- 最简单的,在 appSettings 里面写 -->
        
    <appSettings>
            
    <!-- 这个命令可以删除更高层次中已经定义的所有设置 -->
            
    <clear />
            
    <!-- 这个命令删除一个设置 -->
            
    <remove key="somekey" />
            
    <!-- 添加设置 -->
            
    <add key="key1" value="123" />
            
    <add key="key2" value="456" />
        
    </appSettings>
    </configuration>


    (注:remove 和 clear 同样适用于下面将要提到的 section 和 sectionGroup 定义的元素当中可以用 add 的地方,不再一一阐述)

    三、节处理器 (Section Handlers)

    在配置文件里除了 appSettings, 还可以自已写 XML 格式的配置元素,这些元素叫做节(Section)。当然,如果你自己写一堆复杂的 XML 格式的标签,.NET 自身是不知道如何解析的,因此这里就需要你在指定节的同时,告诉 .NET 如何处理它们,也就是定义“节处理器”(Section Handlers)。

    每一个自定义的节,都需要在 configSections 下面定义它们的节处理器。先来看一个例子:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        
    <!-- 这个里面用来定义节处理器 -->
        
    <configSections>
            
    <section name="dicValues" type="System.Configuration.DictionarySectionHandler" />        
        
    </configSections>
        
        
    <!-- 这是一个自定义的节 -->
        
    <dicValues>        
            
    <add key="key1" value="abc" />
            
    <add key="key2" value="def" />    
        
    </dicValues>
    </configuration>


    这里定义的节使用的是 .NET Framework 里已有的一个类: DictionarySectionHandler.

    因为这些自定义的 SectionHandler 都要提供给 ConfigurationSettings 类使用,因此它们都要实现 IConfigurationSectionHandler 接口。(具体原因可以用 Reflector 查看 ConfigurationSettings 的 GetConfig 方法,一路追踪下去即可找到答案)。

    对于一些常见形式的数据,系统内部定义了几种 handler, 其用法详细叙述如下:

    1. DictionarySectionHandler

    这个类型的 handler 的 GetConfig 方法返回一个 Hashtable 类型的对象。配置方法见上面一个 xml . 我们可以这样写代码来访问其中的设定:

    object o = ConfigurationSettings.GetConfig("dicValues");
    Hashtable ht 
    = (Hashtable) o;

    foreach (string key in ht.Keys)
    {
        MessageBox.Show(key 
    + " = " + ht[key]);
    }


    2. NameValueSectionHandler

    config 文件里设定的方法跟 DictionarySectionHandler 类似:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        
    <configSections>
            
    <section name="nameValues" type="System.Configuration.NameValueSectionHandler" />        
        
    </configSections>
        
        
    <nameValues>        
            
    <add key="key1" value="abc" />
            
    <add key="key2" value="def" />    
        
    </nameValues>
    </configuration>


    但是 GetConfig 方法返回的是一个 NameValueCollection 对象:

    NameValueCollection c = (NameValueCollection) ConfigurationSettings.GetConfig("nameValues");
    foreach (string key in c.Keys)
    {
        MessageBox.Show(key 
    + " = " + c[key]);
    }


    3. SingleTagSectionHandler

    这种类型的元素表现为一个简单元素,只有属性而没有子节点。各个属性的值,将会在读取时存到一个 Hashtable 中返回。配置文件如下:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        
    <configSections>
            
    <section name="singleTag" type="System.Configuration.SingleTagSectionHandler" />        
        
    </configSections>
        
        
    <singleTag a="hello" b="ok" c="haha" />
    </configuration>


    读取:

    Hashtable ht = (Hashtable) ConfigurationSettings.GetConfig("singleTag");
    foreach (string key in ht.Keys)
    {
        MessageBox.Show(key 
    + " = " + ht[key]);
    }


    4. IgnoreSectionHandler

     有时候需要定义一些元素,不准备由 ConfigurationSettings 类来处理,而是在外部处理。这时候为了避免产生异常,用这个 Handler 来声明,可以让 ConfigurationSettings 类读取的时候忽略该元素。这个用得比较少。

    5. 自定义节处理器

    通过实现 IConfigurationSectionHandler 接口,我们可以实现自己的 SectionHandler,在其中保存复杂的设定信息。最常见的是结合序列化来使用。
    比如我们需要在配置文件里保存如下信息:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        
    <!-- 配置节定义部分 -->    
        
    <configSections>
            
    <!-- 指定一个叫做 StudentSettings 的元素,其处理程序是 ConfigDemos.Config1.StudentSettingsSectionHandler.
            注意:这些程序都必须继承自 IConfigurationSectionHandler 接口。
            
            这个元素还可以有两个属性:allowDefinition, allowLocation,其含义自己看 msdn.
            
    -->
            
    <section name="StudentSettings" type="Config1.StudentSettingsSectionHandler, Config1" />
        
    </configSections>
        
        
    <!-- 实际的数据部分 -->
        
    <StudentSettings>
            
    <students>
                
    <Student>
                    
    <name>张三</name>
                    
    <age>20</age>
                
    </Student>
                
    <Student>
                    
    <name>李四</name>
                    
    <age>30</age>
                
    </Student>
            
    </students>
        
    </StudentSettings>
    </configuration>


    我们要在其中保存一组学生的信息,每一个学生有名字,年龄等信息。首先我们实现学生类,以及相应的 settings 类:

    namespace Config1
    {
        
    using System;
        
    using System.Xml.Serialization;

        [Serializable]
        
    public class Student
        {
            
    private string name;
            
    private int age;

            
    // 表示要将此属性序列化为一个元素,而不是属性
            [XmlElement("name"typeof (string))]
            
    public string Name
            {
                
    get { return name; }
                
    set { name = value; }
            }

            
    // 意义同上
            [XmlElement("age"typeof (int))]
            
    public int Age
            {
                
    get { return age; }
                
    set { age = value; }
            }
        }

        [Serializable]
        
    public class StudentSettings
        {
            
    private Student[] students;

            
    // 这个 attribute 指示该属性序列化为 xml 的时候,以多个子元素的形式表现
            [XmlArray("students")]
            
    public Student[] Students
            {
                
    get { return students; }
                
    set { students = value; }
            }
        }
    }


    接着我们实现一个节处理器如下,这个类名字和 config 里定义的是对应的:

    namespace Config1
    {
        
    using System.Configuration;
        
    using System.Xml;
        
    using System.Xml.Serialization;

        
    public class StudentSettingsSectionHandler : IConfigurationSectionHandler
        {
            
    #region IConfigurationSectionHandler 接口的实现

            
    public object Create(object parent, object configContext, XmlNode section)
            {
                XmlSerializer ser 
    = new XmlSerializer(typeof (StudentSettings));
                
    object students = ser.Deserialize(new XmlNodeReader(section));
                
    return students;
            }

            
    #endregion
        }
    }


    好了,我们现在可以用下面的代码来读取设置了。

    object o = ConfigurationSettings.GetConfig("StudentSettings");
    StudentSettings settings 
    = (StudentSettings) o;

    for (int i = 0; i < settings.Students.Length; i++)
    {
        Student student 
    = settings.Students[i];
        MessageBox.Show(student.Name 
    + "" + student.Age);
    }


    以上的实现虽然比较可行,但是考虑到序列化是一个很普遍的操作,而我们在 StudentSettingsSectionHandler 类的 Create 方法里,写死了 StudentSettings 这个类型。这里显然有一种不能重用的 bad smell,比如我现在需要序列化另一个设定类型的实例,岂不是又要重新写一个这样的类?
    解决这个的办法是让设置类的类型变得可以配置,这个其实在 .Text 中已经有了一个很好的实现了,看一下代码:

    namespace Dottext.Framework.Util
    {
        
    using System;
        
    using System.Configuration;
        
    using System.Xml;
        
    using System.Xml.Serialization;
        
    using System.Xml.XPath;

        
    public class XmlSerializerSectionHandler : IConfigurationSectionHandler
        {
            
    public object Create(object parent, object configContext, XmlNode section)
            {
                XPathNavigator nav 
    = section.CreateNavigator();
                
    string typename = (string) nav.Evaluate("string(@type)");
                Type t 
    = Type.GetType(typename);
                XmlSerializer ser 
    = new XmlSerializer(t);
                
    return ser.Deserialize(new XmlNodeReader(section));
            }
        }
    }


    这个代码里读取了当前节点的 type 属性,用反射的方式来创建类型。相应的配置文件里这样写就可以了:

    <BlogConfigurationSettings type="Dottext.Framework.Configuration.BlogConfigurationSettings, Dottext.Framework">
        
    <!-- 内容省略。。。-->
    </BlogConfigurationSettings>


    对于复杂类型的配置,其实并不限于采用序列化的手段来保存类的成员。也可以用手工分析 XML 里子节点的方式来手工创建设置类的实例。DNN 3.2.2 中就是这么做的。不过我个人觉得这个方式比起用序列化来说要麻烦一些。代码的复用性和抽象层次也不如 .Text 这种做法高。

    四、sectionGroup

    上面介绍了如何使用 section 来配置节点的处理方式。其实我们还可以用 sectionGroup,顾名思义 sectionGroup 就是一组 section 的组合,而且这个结构是可以任意嵌套的。这个的用法其实很简单,这里不再罗嗦。我作了一个嵌套的简单例子如下:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        
    <configSections>
            
    <sectionGroup name="neilSettings">
                
    <sectionGroup name="s1">
                    
    <section name="s1a" type="System.Configuration.SingleTagSectionHandler" />
                    
    <section name="s1b" type="System.Configuration.NameValueSectionHandler" />
                
    </sectionGroup>
                
                
    <section name="s2" type="System.Configuration.SingleTagSectionHandler" />
            
    </sectionGroup>
                
        
    </configSections>
        
        
    <neilSettings>
            
    <s1>
                
    <s1a m="x"></s1a>
                
    <s1b>
                    
    <a
  • 相关阅读:
    Xenserver中SR、VBD和VDI之间的关系
    rabbitmq镜像模式设置策略以及高可用
    MySQL主从复制配置详解
    xen 虚拟机挂了,宿主机假死的问题追终,全思路
    XenServer中备份正在运行的虚拟机
    理解 Docker 容器退出码
    prometheus 监控之 进程监控(processexporter)
    XenServer 常见故障处理
    httpCurl封装
    工作中不要为了用系统而用系统
  • 原文地址:https://www.cnblogs.com/cxd4321/p/562485.html
Copyright © 2020-2023  润新知