代码改变世界

通过configSource分割配置文件

2010-09-18 20:25 囧月 阅读(...) 评论(...) 编辑 收藏

一直在使用configSource来分割配置文件内容,却从来没有注意过和它相关的信息,今天特地查了下MSDN。

引用MSDN关于configSource的说明(http://msdn.microsoft.com/zh-cn/library/system.configuration.sectioninformation.configsource(VS.80).aspx):

ConfigSource 属性 (Property) 表示为 ConfigurationSection 对象指定的 configSource 属性 (Attribute) 的值,该对象与 SectionInformation 对象关联。

ConfigurationSection 实现可以指定一个单独的文件,并在其中定义该节的配置设置(可选)。它可以多种方式发挥作用:

使用包含文件可以使配置文件具有更加逻辑化和模块化的结构。

文件访问安全和权限可用于限制对配置设置节的访问。

未在应用程序初始化期间使用的包含文件中的设置,可以在不重新启动的情况下进行修改并重新加载。

下面的示例演示如何在配置文件中使用此属性,来指定 pages 节是在外部包含文件中定义的:

<pages configSource="pages.config"/>

如果配置包含文件中的任何设置需要在修改后重新启动应用程序,请将 RestartOnExternalChanges 属性设置为 true。

 

    从引用的内容可以看到,每个ConfigurationSection节点都可以指定configSource属性以实现配置信息外置。而对于外部配置包含文件,通过RestartOnExternalChanges属性来指定是否需要在对外部配置包含文件进行更改后重新启动应用程序。

    通过查看SectionInformation的构造函数可以看到,RestartOnExternalChanges默认值为true,在默认情况下外部配置包含文件的更改会引起应用程序的重启。如果不想在更改外部配置包含文件之后重启应用程序,则需手动设置RestartOnExternalChanges值为true。

    通过查看machine.config可以看出,除了appSettings、system.data.dataset节点的外部配置包含文件,其他ConfigurationSection节点的外部配置包含文件的更改均会引起应用程序的重启。

    对于自定义ConfigurationSection的外部配置包含文件,同样需要手动指定RestartOnExternalChanges为false,才不会引起应用程序的重启:

<section name="mySection" type="WebApplication1.MySection" requirePermission="false" restartOnExternalChanges="false"/>

    对于appSettings节点,还有一个file属性可以实现配置信息外置,不过它是会缓存配置信息的,外部配置包含文件的更改不会使应用程序重启,同时也无法应用程序重启之前体现出来,感觉比较鸡肋。

    不管是否使用了configSource,每次都会重新读取配置文件,在性能上依然存在影响。可以考虑在使用configSource基础上加以缓存,并使用FileSystemWatcher监听文件更改(注意2次change问题)。

    简单例子:

    public class MyConfigurationManager
    {
        private static Dictionary<string, object> sectionCache = new Dictionary<string, object>();
        private static Dictionary<string, string> fileNameCache = new Dictionary<string, string>();
        private static List<string> pathCache = new List<string>();

        static void watcher_Changed(object sender, FileSystemEventArgs e)
        {
            if (fileNameCache.ContainsKey(e.Name))
            {
                string sectionName = fileNameCache[e.Name];
                sectionCache[sectionName] = ConfigurationManager.GetSection(sectionName);
            }
        }

        private static void AddWatch(string watchPath)
        {
            FileSystemWatcher watcher = new FileSystemWatcher();
            watcher.Path = watchPath;
            watcher.Filter = "*.config";
            watcher.NotifyFilter = NotifyFilters.Size;
            watcher.Changed += new FileSystemEventHandler(watcher_Changed);
            watcher.EnableRaisingEvents = true;
        }

        public static void AddWatch(string sectionName, string fileFullName)
        {
            string filePath = Path.GetDirectoryName(fileFullName);
            if (!pathCache.Contains(filePath))
            {
                pathCache.Add(filePath);
                AddWatch(filePath);
            }
            fileNameCache.Add(Path.GetFileName(fileFullName), sectionName);
        }

        public static object GetSection(string sectionName)
        {
            if (!sectionCache.ContainsKey(sectionName))
            {
                sectionCache.Add(sectionName, ConfigurationManager.GetSection(sectionName));
            }
            return sectionCache[sectionName];
        }

        public static T GetSection<T>(string sectionName) where T : ConfigurationSection
        {
            return (T)GetSection(sectionName);
        }
    }

    public class MySection : ConfigurationSection
    {
        [ConfigurationProperty("settings")]
        public MySettingCollection Settings
        {
            get
            {
                return (MySettingCollection)base["settings"];
            }
        }
    }

    public class MySettingCollection : ConfigurationElementCollection
    {

        protected override ConfigurationElement CreateNewElement()
        {
            return new MySetting();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return (element as MySetting).Name;
        }

        new public MySetting this[string key]
        {
            get { return (MySetting)base.BaseGet(key); }
        }
    }

    public class MySetting : ConfigurationElement
    {
        [ConfigurationProperty("name")]
        public string Name
        {
            get { return base["name"] as string; }
            set { base["name"] = value; }
        }

        [ConfigurationProperty("value")]
        public string Value
        {
            get { return base["value"] as string; }
            set { base["value"] = value; }
        }
    }

相关配置web.config:

<configuration>
	<configSections>
		<section name="mySection" type="WebApplication1.MySection" requirePermission="false" restartOnExternalChanges="false"/>
	</configSections>
	<mySection configSource="config\mySection.config"/>
</configuration>

mySection.config:

<mySection>
    <settings>
        <add name="test" value="mysection12" />
    </settings>    
</mySection>

测试代码aspx:

        mysection:<asp:Literal ID="Literal2" runat="server"></asp:Literal>
        <br />
        session:<asp:Literal ID="Literal3" runat="server"></asp:Literal>
        <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
        <asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="SetSession" />
        <br />
        <asp:Button ID="Button2" runat="server" Text="Refresh" 
            onclick="Button2_Click" />

测试代码aspx.cs:

    public partial class test : System.Web.UI.Page
    {
        static test()
        {
            MyConfigurationManager.AddWatch("mySection", HttpContext.Current.Server.MapPath("~/config/mySection.config"));
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            Literal2.Text = MyConfigurationManager.GetSection<MySection>("mySection").Settings["test"].Value;
            Literal3.Text = Session["test"] == null ? string.Empty : Session["test"].ToString();
        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            Session["test"] = TextBox1.Text;
        }

        protected void Button2_Click(object sender, EventArgs e)
        {

        }
    }

以上代码仅供测试,可以根据实际需要加以改造。