• 掌握 .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
  • 相关阅读:
    Smart Client Architecture and Design Guide
    Duwamish密码分析篇, Part 3
    庆贺发文100篇
    .Net Distributed Application Design Guide
    New Introduction to ASP.NET 2.0 Web Parts Framework
    SPS toplevel Site Collection Administrators and Owners
    来自Ingo Rammer先生的Email关于《Advanced .Net Remoting》
    The newsletter published by Ingo Rammer
    深度探索.Net Remoting基础架构
    信道、接收器、接收链和信道接受提供程序
  • 原文地址:https://www.cnblogs.com/cxd4321/p/562485.html
Copyright © 2020-2023  润新知