首页 > 代码库 > configSections

configSections

   由于最近一个项目的数据库变动比较频繁, 为了减少数据层的负担, 打算采用.net的MVC框架, 使用LINQ对付数据层.
 
    这个框架的web.config文件里出现了configSections section, 这个之前没有留意, 乘着项目不是很急, 顺势把它给搞懂, 花了一下午时间, 终于搞定, 有点心得, 写下来以供面试官翻阅.
 
    asp.net为啥要引用configSections?
因为用户的一些对象, 可能在config里进行配置, 但是config怎么能随便让你添加自己的节点呢! 不行你自己试试, 在任何位置添加任何没有申明的节点, 系统都不会让你通过, 更不会让你去读它了, 当然, 你打算在别的xml文件里添加节点, 然后读出来, 创建对象, 这个没问题.
 
为了系统能有组织的管理用户的在配置文件里的自定义信息, 就要使用configSections了, 分3步走:
  1. 创建一个实现IConfigurationSectionHandler的类
 
  2. 在config文件里添加如下节点:
 
  <configuration>
              <configSections>
                   <section name="NameM" type="LearningConfiguration.NameSectionHandler"/>
              </configSections>
              <NameM>
                   <Add key="name1" firstname="Jim" lastname="w1"/>
                   <Add key="name2" firstname="Chris" lastname="w2"/>
               </NameM>
    
        说明: 在configSections下添加一个section子节点, 写好标示名和类型, 然后在紧接这下面实现这个标示名的细节内容,就是我们当初想添加的自己的自定义信息了.
       
    3. 实现IConfigurationSectionHandler唯一的方法: public object Create(object parent, object configContext, XmlNode section), 这里的最后一个参数就是自定义信息那个节点了, 粗体部分. 通过处理这堆信息, 返回你要的对象.
 
       4. 使用这个返回的对象,通过System.Configuration.ConfigurationManager.GetSection()方法.
 
以下是示例代码:
(NameSectionHandler.cs)

namespace LearningConfiguration
{
    public class NameSectionHandler : IConfigurationSectionHandler
    {
        #region IConfigurationSectionHandler Members
        public object Create(object parent, object configContext, XmlNode section)
        {
            Dictionary<string, NameManagement> names = new Dictionary<string, NameManagement>();
            
            string key = string.Empty;
            string firstName = string.Empty;
            string lastName = string.Empty;
            foreach (XmlNode childNode in section.ChildNodes)
            {
                if (childNode.Attributes["key"] != null)
                {
                    key = childNode.Attributes["key"].Value;

                    if (childNode.Attributes["firstname"] != null)
                    {
                        firstName = childNode.Attributes["firstname"].Value;
                    }
                    else
                    {
                        firstName = string.Empty;
                    }
                    if (childNode.Attributes["lastname"] != null)
                    {
                        lastName = childNode.Attributes["lastname"].Value;
                    }
                    else
                    {
                        lastName = string.Empty;
                    }
                    names.Add(key, new NameManagement(firstName, lastName));
                }
            }
            return names;
        }
        #endregion
    }
}
(NameManagement.cs)
namespace LearningConfiguration
{
    public class NameManagement
    {
        string _firstName;
        public string FirstName
        {
            get { return this._firstName; }
            set { this._firstName = value; }
        }
        string _lastName;
        public string LastName
        {
            get { return this._lastName; }
            set { this._lastName = value; }
        }
        public NameManagement(string firstName, string lastName)
        {
            this.FirstName = firstName;
            this.LastName = lastName;
        }
        public string RetrieveFullName()
        {
            return string.Format("{0} {1}", this.FirstName, this.LastName);
        }
    }
}
(Default.aspx.cs)
namespace LearningConfiguration
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Dictionary<string, NameManagement> names = ConfigurationManager.GetSection("NameM") as Dictionary<string, NameManagement>;
            if (names != null)
            { 
                foreach(string key in names.Keys)
                {
                    NameManagement name = names[key] as NameManagement;
                    Response.Write(name.RetrieveFullName());
                }
            }
        }
    }
}
 与以上调用方式一样:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Configuration;
using System.Collections.Specialized;
using LearningConfiguation;

public partial class Default4 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Dictionary<String, NameManagement> names = System.Configuration.ConfigurationManager.GetSection("test/aa") as Dictionary<String, NameManagement>;
        Dictionary<String, NameManagement>.KeyCollection keys = names.Keys;
        foreach (String key in keys)
        {
            NameManagement nm = names[key] as NameManagement;
            Response.Write(nm.FirstName);
            Response.Write("<br>");
            Response.Write(nm.LastName);
        }
    }
}
 
1.为什么需要自定义节点

为了增加应用程序的可移植性,通常网站需要配置一些自定义的节点,例如:文件上传的路径等,再深入的应用,可以定义工厂方法需要创建的类。

 

2.configSections使用方法

configSections节点下定义自定义节点可以帮我们实现我们自己的节点。

首先定义自己的节点,定义方法如下:

<configSections>
    <sectionGroup name="section group name">
        <section name="section name" type="configuration section handler class" />
    </sectionGroup>
</configSections>


定义自己的节点必须在configSections节点。

sectionGroup 元素充当 section 元素的容器。

section 元素将配置节处理程序与配置元素或节关联。由于 ASP.NET 不对如何处理配置文件内的设置作任何假设,因此这非常必要。但 ASP.NET 会将配置数据的处理委托给配置节处理程序,(稍候说明。)每个 section 元素均标识一个配置节或元素。可以在 sectionGroup 元素中对 section 元素进行逻辑分组,以对 section 元素进行组织并避免命名冲突。section 和 sectionGroup 元素包含在 configSections 元素中。

sectionGroup节点属性:

name:必选的 String 属性,这是 group 元素在配置文件的节设置区域中使用的名称。

section节点属性:

name:必选的 String 属性,指定与 type 属性中指定的配置节处理程序关联的配置节或元素的名称。这是该元素在配置文件的节设置区域中使用的名称。

type:必选的 String 属性,指定用来执行如下操作的配置节处理程序类的名称:处理在 name 属性中指定的节或元素中的配置设置。

 


现在定义好了自己的节点,可以使用该节点了。使用方法如下:

<section group name>
    <section name>
        <add key="key1" value="http://www.mamicode.com/value1" />
    </section name>
</section group name>


定义好了自己的节点,如何读取节点信息呢?

以下是msdn上的原话:

您可以用自己的 XML 配置元素来扩展标准的 ASP.NET 配置设置集。若要完成该操作,您必须创建自己的配置节处理程序。

该处理程序必须是一个实现 System.Configuration.IConfigurationSectionHandler 接口或 System.Configuration.ConfigurationSection 类的 .NET Framework 类。

节处理程序解释并处理 Web.config 文件特定部分中 XML 配置元素中定义的设置,并根据配置设置返回适当的配置对象。处理程序类返回的配置对象可以是任何数据结构;它不限于任何基配置类或配置格式。ASP.NET 使用该配置对象,以对自定义配置元素进行读取和写入。


上面这段话的意思就是说,我们要定义一个类,这个类要继承自System.Configuration.IConfigurationSectionHandler 接口或 System.Configuration.ConfigurationSection 类。


然后用这个类来处理我们自定义的节点。

我们看到System.Configuration.IConfigurationSectionHandler接口中,只有一个方法:


//创建配置节处理程序
Object Create (Object parent, Object configContext, XmlNode section)

 


返回值

创建的节处理程序对象。

 


这个类是干什么用的呢?让我们通过一个例子来看看。

 


首先,我们新建一个网站项目,并在web.config中加入以下节点:


<configSections>
    <sectionGroup name="WebSiteInfo">
        <section name="basicInfo" type="ConfigurationSectionTest.WebSiteInfoHandler"/>
        <section name="fileUpload" type="ConfigurationSectionTest.WebSiteInfoHandler"/>
    </sectionGroup>
</configSections>

<WebSiteInfo>
    <basicInfo>
        <add key="name" value="http://www.mamicode.com/huchen‘s homepage"/>
        <add key="version" value="http://www.mamicode.com/1.0"/>
    </basicInfo>
    <fileUpload>
        <add key="fileUploadPath" value="http://www.mamicode.com/E://MyHomePage//Web//Upload//"/>
        <add key="fileUploadSizeMax" value="http://www.mamicode.com/2M"/>
    </fileUpload>
</WebSiteInfo>

 

以上我们在WebSiteInfo节点下定义了两个节点basicInfo和fileUpload,并定义了节点处理程序类ConfigurationSectionTest.WebSiteInfoHandler,并且随后运用了我们定义的节点。


我们来看看节点处理程序ConfigurationSectionTest.WebSiteInfoHandler。

任意建立一个项目,新建一个类,或者直接在App_Code里新建一个类,如下:

并在Create函数中设置一个断点。

 

 

namespace ConfigurationSectionTest
{
    /// <summary>
    ///WebSiteInfoHandler 的摘要说明
    /// </summary>
    public class WebSiteInfoHandler : IConfigurationSectionHandler
    {
        public WebSiteInfoHandler()
        {
            //
            //TODO: 在此处添加构造函数逻辑
            //

        }

        #region IConfigurationSectionHandler 成员

        public object Create(object parent, object configContext, System.Xml.XmlNode section)
        {
           //这里我们首先返回个hello,并且在此处设置一个断点。看看程序什么时候执行到这。
            return "hello";
        }

        #endregion
    }

}

 

然后在Default.aspx的Page_Load事件处理程序中去访问我们自定义的节点,并在ConfigurationSettings.GetConfig("WebSiteInfo/basicInfo"); 这条语句上设置断点。


protected void Page_Load(object sender, EventArgs e)
{
    Object o = ConfigurationSettings.GetConfig("WebSiteInfo/basicInfo");
}

 


好了,我们启动调试,看到程序首先执行到ConfigurationSettings.GetConfig("WebSiteInfo/basicInfo");这句。

然后执行到ConfigurationSectionTest.WebSiteInfoHandler中的Create函数。

我们再看看这时处理函数中参数的值:

parent为null

configContext 为配置上下文对象。

section 的InnerXml为<add key="name" value="http://www.mamicode.com/huchen‘s homepage" /><add key="version" value="http://www.mamicode.com/1.0" />

 


按F11继续执行return "hello", 继续执行...

在执行到Object o = ConfigurationSettings.GetConfig("WebSiteInfo/basicInfo")后面的“}“,我们发现o的值为”hello”。 


相信您已经明白的差不多了,当读取自定义节点的内容时,程序去执行我们定义的节点处理程序,并把节点中的内容传给Create函数中的参数。然后我们在Create中自己处理节点下的内容,并返回我们格式化后的节点内容给调用者,也就是ConfigurationSettings.GetConfig("WebSiteInfo/basicInfo")。

注意:程序第一次运行的时候可以调试到Create这个方法,第二次运行的时候就调试不到了,这是因为配置文件时缓存了起来.

只有改变了配置文件或者继承自IConfigurationSectionHandler的类才会重新调用.

 

好,知道这些以后,我们就来完善我们的代码,我们在Create中处理传进来的节点内容。


为了简单起见,我们引入两个类NameValueCollection,NameValueSectionHandler。

NameValueCollection:表示可通过键或索引访问的关联 String 键和 String 值的集合。

NameValueSectionHandler:提供配置节中的名称/值对配置信息。NameValueSectionHandler 这个类也继承IConfigurationSectionHandler
反编译可以看出NameValueSectionHandler 的Create方法把参数section的结果转化成了一个集合,就是NameValueCollection。

那么我们可以在节点处理程序中的Create函数中写如下代码:


NameValueCollection configs;
NameValueSectionHandler baseHandler = new NameValueSectionHandler();
configs =(NameValueCollection)baseHandler.Create(parent,configContext,section);
Return configs;

 

这样我们就可以这样访问我们的节点了:


string myWebSiteName = ((NameValueCollection)ConfigurationSettings.GetConfig("WebSiteInfo/basicInfo"))["name"];


在Default.aspx的Page_Load事件处理程序中添加如下代码:


string myWebSiteName = ((NameValueCollection)ConfigurationSettings.GetConfig("WebSiteInfo/basicInfo"))["name"];
Response.Write(myWebSiteName);

 


Ctrl+F5运行,可以看到页面输出了huchen‘s homepage