Chinaunix首页 | 论坛 | 博客
  • 博客访问: 175289
  • 博文数量: 89
  • 博客积分: 30
  • 博客等级: 民兵
  • 技术积分: 565
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-24 10:16
文章分类

全部博文(89)

文章存档

2013年(1)

2012年(1)

2011年(8)

2010年(45)

2009年(34)

我的朋友

分类: C/C++

2009-09-16 17:11:40

掌握 .NET 1.1 的配置文件用法(app.config 或者 web.config )(一)
2007-07-02 11:09

         在 .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 >
        

        
< 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>
        

        
<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>
        

        
<section name="StudentSettings" type="Config1.StudentSettingsSectionHandler, Config1" />
    
configSections>
    
    

    
<StudentSettings>
        
<students>
            
<Student>
                
<name>张三name>
                
<age>20age>
            
Student>
            
<Student>
                
<name>李四name>
                
<age>30age>
            
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; }
         }
     }
}
 
掌握 .NET 1.1 的配置文件用法(二)
2007-07-02 11:12
接着我们实现一个节处理器如下,这个类名字和 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 中已经有了一个很好的实现了,看一下代码:

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>
                
<add key="name" value="zhangsan" />
                
<add key="age" value="20" />
            
s1b>
        
s1>
        
<s2 a="1" b="2" c="3">s2>
    
neilSettings>
configuration>

要读取这个设置,我们这么写就可以了:

Hashtable s1a = (Hashtable) ConfigurationSettings.GetConfig("neilSettings/s1/s1a");
MessageBox.Show(s1a.Count.ToString());
// 1

NameValueCollection s1b
= (NameValueCollection) ConfigurationSettings.GetConfig("neilSettings/s1/s1b");
MessageBox.Show(s1b.Count.ToString());
// 2

Hashtable s2
= (Hashtable) ConfigurationSettings.GetConfig("neilSettings/s2");
MessageBox.Show(s2.Count.ToString());
// 3


注意在 GetConfig 方法中,我们只要传入正确的 XPath 语法以找出所需节点就可以了。
除了用程序自身的 config 文件来存储配置,我们还可以自己来实现可读写的配置文件,存储复杂的设置。在这方面,ASP.NET 1.1 的 StarterKit 中有一个很好的实现,其主要原理是利用了强类型 DataSet 的一些功能。那样实现有一个好处,就是在 VS.NET 设计器里有很好的支持。可视化程度比较高。下一次我会详细来分析 ASP.NET 1.1 StarterKit 的配置实现原理。

阅读(271) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:在C#中读写INI配置文件

给主人留下些什么吧!~~