您的位置:首页 > 其它

可实现自动重载配置文件读取类

2011-12-04 13:07 651 查看
在开发的过程中,我们可能会在网站的运行中对配置文件做一些修改,并且这些修改还要求实时反映出来。或许在 Web.config 中添加是一个不错的方案,但是如果配置太多的话,我想你也不想把它弄得太臃肿。那这个时候应该怎么办呢?或许我这这篇文章能够帮到你。

 

我现在遇到的问题是公司把网站拆分了,开发的时候每组就是每组的,但是正式线上可能还需要把其它组的配置写进去,最后因此导致的问题不胜枚举。无奈,就想到了把配置文件分开,每个组一个就不会出现这样的问题了。但是问题又来了,我怎么能在配置文件变动后,网站会自动重新提取最近的内容呢?后来从这篇文章《使用.NET FileSystemWatcher对象监控C#目录改变》得到些灵感,将这个东西做了出来。

 

现在,我将我的做法贡献出来,如果对你有用,请留下你的足迹;如果你觉得不好,也可以给我留言,我好进一步改进。我相信人民大众的力量是无限的,如果你对此有更好的改动,请你在下面贴出来或者回复我的邮件(Lenic@live.cn),我也相信你不会默默无闻的。

 

废话说了一堆,还是先看看这个类怎么用吧。

private static XmlParser xml = null;


[code]static Default()


{


var path = HttpContext.Current.Server.MapPath(".\\TestData.xml");


xml = XmlParser.Load(path, new string[] { "orange" });


}

[/code]

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

基本上是将配置文件的绝对路径当作参数传递进去就可以了,第二个参数是有额外用途的,这个一会儿再说。


 

我们需要读取这个 XML 文件的内容,预先看一下其内容:

<?xml version="1.0" encoding="utf-8"?>


[code]<apple phone="true">


<items>


<orange id="1">


<name>橘子1</name>


</orange>


<orange id="o2">


<name>橘子2</name>


</orange>


<orange id="o3">


<name>橘子3</name>


</orange>


<orange id="o4">


<name>橘子4</name>


</orange>


</items>


<id>a1</id>


<name>苹果</name>


</apple>

[/code]

 

在写配置的时候,我们一般是将其写入到节点的 Attribute 中的,比如文件中的 orange 节点的 id 和 apple 节点的 phone 两个配置信息。除此之外,对于哪些文本特别长的 Attribute ,会考虑将其作为子节点写入,比如文件中的 name 节点,分别就是 apple 和 orange 的配置信息。

 

如果通过我的 XmlParser 类读取,可以统一使用 GetString 方法读取,示例如下:

string a = xml.GetString("name");


[code]string b = xml.GetString("phone");

[/code]

当然,你可以通过重载获得更大的控制权。

 

另外一个问题,我想获取 orange 节点如何做到?回到 Load 方法,其第二个参数是一个字符串数组,我解释一下其作用:指定 apple 节点下逐级复杂节点的 Type 。在 Load 时会把这些 Type 作为递归 XmlParser 填充当前对象的 Items 属性下。这是正统的用法,如果你有意见,请先保留,下面会有说明。

 

我在这里举例说明:XML 配置文件中,apple 作为根节点,下面有个 items 包含了一组 orange ,在 Load 的时候我指定了 orange 作为第一级的复杂节点,使用的时候就可以通过下面的语句进行读取:

var c = xml.Items[0].GetString("name"); // name = 橘子1



到此,我相信你了解了第二个参数的作用。在不用递归子级的时候,可以设置第二个参数为 null 。

 

还有一些需求是不指定子级,在运行时动态分析。比如在 apple 节点下直接添加 orange 节点,此时如果想要获取 orange 的节点信息,你可以求助于

InnerParsers 属性,该属性包含了 apple 下的所有子节点,当然也就有这个非正统的 orange 节点。顺带提一句,Items 属性中只会包含在 items 子节点下的所有符合 orange 名称的节点。orange 通过 Load 方法第二个参数指定,逐级递归时依然有效。

 

罗嗦一句,EqualComparer 类的说明我正在写,稍等大概半小时就可以出来了,由此导致的不便还请您谅解。最后,贴上代码,毕竟代码在程序员的世界是最清楚明了的语言:

using System;


[code]using System.Collections.Generic;


using System.Diagnostics;


using System.IO;


using System.Linq;


using System.Xml.Linq;


using Lenic.Core;


 


namespace Lenic


{


/// <summary>


/// 配置文件分析方式


/// </summary>


public enum XmlParserTypes


{


/// <summary>


/// 【缺省值】自适应检索方式(分析顺序:Attriburtes、Children)


/// </summary>


Auto = 0,


/// <summary>


/// 从 Attributes 中检索


/// </summary>


Attribute = 1,


/// <summary>


/// 从 Children 中检索


/// </summary>


InnerElement = 2,


}


 


/// <summary>


/// 尝试从字符串中解析 T 类型, 分析成功时返回 <c>true</c> , 分析结果在 result 中.


/// </summary>


/// <typeparam name="T">待分析的目标类型.</typeparam>


/// <param name="obj">待分析的源字符串.</param>


/// <param name="result">分析完成的结果.</param>


/// <returns><c>true</c> 表示分析成功; 否则返回 <c>false</c> .</returns>


public delegate bool TryParse<T>(string obj, out T result);


 


/// <summary>


/// 配置文件分析基类


/// </summary>


[DebuggerStepThrough]


[DebuggerDisplay("Type = {Type}, ItemsCount = {Items.Length}")]


public class XmlParser


{


#region Fields


[DebuggerBrowsable(DebuggerBrowsableState.Never)]


private string filePath = string.Empty;


[DebuggerBrowsable(DebuggerBrowsableState.Never)]


private WeakReference element = null;


[DebuggerBrowsable(DebuggerBrowsableState.Never)]


private Dictionary<string, string> attributes = null;


[DebuggerBrowsable(DebuggerBrowsableState.Never)]


private Dictionary<string, string> children = null;


[DebuggerBrowsable(DebuggerBrowsableState.Never)]


private XmlParser[] items = null;


[DebuggerBrowsable(DebuggerBrowsableState.Never)]


private string type = null;


[DebuggerBrowsable(DebuggerBrowsableState.Never)]


private string[] path = null;


[DebuggerBrowsable(DebuggerBrowsableState.Never)]


private XmlParser[] innerParsers = null;


[DebuggerBrowsable(DebuggerBrowsableState.Never)]


private FileSystemWatcher fsw = null;


[DebuggerBrowsable(DebuggerBrowsableState.Never)]


private string text = null;


#endregion


 


#region Properties


private XElement Element


{


get


    {


if (element.IsAlive)


return element.Target as XElement;


else


throw new NullReferenceException("The current element is not effective.");


}


}


 


/// <summary>


/// 获取当前节点的类型.


/// </summary>


public string Type


{


get { return type; }


}


 


/// <summary>


/// 获取当前节点的串连文本内容.


/// </summary>


public string Text


{


get


    {


if (text == null)


text = Element.Value;


return text;


}


}


 


/// <summary>


/// 获取包含的内层元素集合(items 节点).


/// </summary>


[DebuggerDisplay("Length = {Items.Length}")]


public XmlParser[] Items


{


get


    {


if (items == null)


{


var currPath = path.Length > 0 ? path[0] : string.Empty;


var innerPath = path.Length > 0 ? path.Skip(1) : Enumerable.Empty<string>();


items = Element.Elements()


.Where(p => p.Name.LocalName.Equals("items", StringComparison.CurrentCultureIgnoreCase))


.Elements()


.Where(p => p.Name.LocalName.Equals(currPath, StringComparison.CurrentCultureIgnoreCase))


.Select(p => new XmlParser(new WeakReference(p, true), innerPath))


.ToArray();


 


WeakReference wr = new WeakReference(1, true);


}


return items;


}


}


 


/// <summary>


/// 获取当前节点的所有属性集合.


/// </summary>


[DebuggerDisplay("Count = {Attributes.Count}")]


public Dictionary<string, string> Attributes


{


get


    {


if (attributes == null)


{


attributes = new Dictionary<string, string>(new EqualComparer<string>((x, y) =>


string.Compare(x, y, StringComparison.CurrentCultureIgnoreCase) == 0));


 


foreach (var item in Element.Attributes())


    {


if (!attributes.ContainsKey(item.Name.LocalName))


attributes.Add(item.Name.LocalName, item.Value);


}


}


return attributes;


}


}


 


/// <summary>


/// 获取当前节点的所有内含元素集合(不包含 items 节点).


/// </summary>


[DebuggerDisplay("Count = {Children.Count}")]


public Dictionary<string, string> Children


{


get


    {


if (children == null)


{


Func<string, bool> comparer = p => true;


if (path.Length > 0)


    {


var currPath = path[0];


comparer = p => !p.Equals(currPath, StringComparison.CurrentCultureIgnoreCase);


}


 


children = new Dictionary<string, string>(new EqualComparer<string>((x, y) =>


string.Compare(x, y, StringComparison.CurrentCultureIgnoreCase) == 0));


 


foreach (var item in Element.Elements()


.Where(p => !p.Name.LocalName.Equals("items", StringComparison.CurrentCultureIgnoreCase) &&


comparer(p.Name.LocalName)))


    {


if (!children.ContainsKey(item.Name.LocalName))


children.Add(item.Name.LocalName, item.Value);


}


}


return children;


}


}


 


/// <summary>


/// 获取当前节点的所有内含元素集合(所有节点).


/// </summary>


[DebuggerDisplay("Length = {InnerParsers.Length}")]


public XmlParser[] InnerParsers


{


get


    {


if (innerParsers == null)


{


var innerPath = path.Skip(1).ToArray();


innerParsers = Element.Elements()


                  .Select(p => new XmlParser(new WeakReference(p, true), innerPath))


                  .ToArray();


}


return innerParsers;


}


}


#endregion


 


#region Entrance


/// <summary>


/// 初始化新建一个 <see cref="XmlParser"/> 类的实例对象.


/// </summary>


/// <param name="filePath">文件路径.</param>


/// <param name="path">内层元素的路径.</param>


/// <returns>一个 <see cref="XmlParser"/> 类的实例对象</returns>


public static XmlParser Load(string filePath, IEnumerable<string> path)


{


return new XmlParser(filePath, path);


}


 


/// <summary>


/// 初始化新建一个 <see cref="XmlParser"/> 类的实例对象.


/// </summary>


/// <param name="filePath">文件路径.</param>


/// <param name="path">内层元素的路径.</param>


public XmlParser(string filePath, IEnumerable<string> path)


{


this.filePath = Path.GetFullPath(filePath);


 


InitInstance(new WeakReference(XDocument.Load(this.filePath).Root, true), path);


InitFileSystemWatcher();


}


 


private XmlParser(WeakReference element, IEnumerable<string> path)


{


InitInstance(element, path);


}


 


private void InitInstance(WeakReference reference, IEnumerable<string> path)


{


this.element = reference;


this.type = ((XElement)element.Target).Name.LocalName;


this.path = path == null ? new string[] { } : path.ToArray();


 


this.OnXmlFileChanged = () =>


    {


if (items != null)


{


foreach (var item in items)


item.XmlFileChanged();


items = null;


}


 


if (innerParsers != null)


{


foreach (var item in innerParsers)


item.XmlFileChanged();


innerParsers = null;


}


 


element.Target = null;


type = null;


text = null;


attributes = null;


children = null;


if (!string.IsNullOrEmpty(filePath))


        InitInstance(new WeakReference(XDocument.Load(this.filePath).Root, true), path);


};


}


 


private void InitFileSystemWatcher()


{


fsw = new FileSystemWatcher


    {


Filter = Path.GetFileName(filePath),


IncludeSubdirectories = false,


NotifyFilter = NotifyFilters.LastWrite,


Path = Path.GetDirectoryName(filePath),


};


fsw.Changed += (sender, e) => XmlFileChanged();


fsw.EnableRaisingEvents = true;


}


#endregion


 


#region Events


/// <summary>


/// 在原始 XML 文件内容发生改变的时候发生


/// </summary>


public event Action OnXmlFileChanged;


 


/// <summary>


/// 引发 OnXmlFileChanged 事件


/// </summary>


protected void XmlFileChanged()


{


if (OnXmlFileChanged != null)


OnXmlFileChanged();


}


#endregion


 


#region Parse Integer


/// <summary>


/// 根据指定的条件获取属性的值(自适应在 Attribute 和 InnerElement 中寻找,缺省值为 <c>int.MinValue</c> ,并且忽略大小写).


/// </summary>


/// <param name="name">属性名称.</param>


/// <param name="ignoreCase">如果设置为 <c>true</c> 表示忽略名称大小写检索.</param>


/// <returns>检索结果.</returns>


public int GetInteger(string name)


{


return GetInteger(name, int.MinValue, XmlParserTypes.Auto);


}


 


/// <summary>


/// 根据指定的条件获取属性的值(自适应在 Attribute 和 InnerElement 中寻找).


/// </summary>


/// <param name="name">属性名称.</param>


/// <param name="defaultValue">未找到属性时返回的缺省值.</param>


/// <returns>检索结果.</returns>


public int GetInteger(string name, int defaultValue)


{


return GetInteger(name, defaultValue, XmlParserTypes.Auto);


}


 


/// <summary>


/// 根据指定的条件获取属性的值.


/// </summary>


/// <param name="name">属性名称.</param>


/// <param name="defaultValue">未找到属性时返回的缺省值.</param>


/// <param name="type">属性值检索方式.</param>


/// <returns>检索结果.</returns>


public int GetInteger(string name, int defaultValue, XmlParserTypes type)


{


var value = GetString(name, null, type);


if (string.IsNullOrEmpty(value))


return defaultValue;


 


int result = 0;


if (int.TryParse(value, out result))


return result;


 


return defaultValue;


}


#endregion


 


#region Parse T


/// <summary>


/// 根据指定的条件获取属性的值(自适应在 Attribute 和 InnerElement 中寻找,缺省值为 <c>default(T)</c> ,并且忽略大小写).


/// </summary>


/// <typeparam name="T">待分析的类型.</typeparam>


/// <param name="name">属性名称.</param>


/// <param name="tryParse">字符串尝试分析委托.</param>


/// <returns>检索结果.</returns>


public T GetValue<T>(string name, TryParse<T> tryParse)


{


return GetValue<T>(name, tryParse, default(T), XmlParserTypes.Auto);


}


 


/// <summary>


/// 根据指定的条件获取属性的值(自适应在 Attribute 和 InnerElement 中寻找).


/// </summary>


/// <typeparam name="T">待分析的类型.</typeparam>


/// <param name="name">属性名称.</param>


/// <param name="tryParse">字符串尝试分析委托.</param>


/// <param name="defaultValue">未找到属性时返回的缺省值.</param>


/// <returns>检索结果.</returns>


public T GetValue<T>(string name, TryParse<T> tryParse, T defaultValue)


{


return GetValue<T>(name, tryParse, defaultValue, XmlParserTypes.Auto);


}


 


/// <summary>


/// 根据指定的条件获取属性的值.


/// </summary>


/// <typeparam name="T">待分析的类型.</typeparam>


/// <param name="name">属性名称.</param>


/// <param name="tryParse">字符串尝试分析委托.</param>


/// <param name="defaultValue">未找到属性时返回的缺省值.</param>


/// <param name="type">属性值检索方式.</param>


/// <returns>检索结果.</returns>


public T GetValue<T>(string name, TryParse<T> tryParse, T defaultValue, XmlParserTypes type)


{


var value = GetString(name, null, type);


if (string.IsNullOrEmpty(value))


return defaultValue;


 


T result = default(T);


if (tryParse(value, out result))


return result;


 


return defaultValue;


}


#endregion


 


#region Parse String


/// <summary>


/// 根据指定的条件获取属性的值(自适应在 Attribute 和 InnerElement 中寻找,缺省值为 <c>null</c> ,并且忽略大小写).


/// </summary>


/// <param name="name">属性名称.</param>


/// <param name="ignoreCase">如果设置为 <c>true</c> 表示忽略名称大小写检索.</param>


/// <returns>检索结果.</returns>


public string GetString(string name)


{


return GetString(name, null, XmlParserTypes.Auto);


}


 


/// <summary>


/// 根据指定的条件获取属性的值(自适应在 Attribute 和 InnerElement 中寻找).


/// </summary>


/// <param name="name">属性名称.</param>


/// <param name="defaultValue">未找到属性时返回的缺省值.</param>


/// <returns>检索结果.</returns>


public string GetString(string name, string defaultValue)


{


return GetString(name, defaultValue, XmlParserTypes.Auto);


}


 


/// <summary>


/// 根据指定的条件获取属性的值.


/// </summary>


/// <param name="name">属性名称.</param>


/// <param name="defaultValue">未找到属性时返回的缺省值.</param>


/// <param name="type">属性值检索方式.</param>


/// <returns>检索结果.</returns>


public string GetString(string name, string defaultValue, XmlParserTypes type)


{


var target = string.Empty;


 


if (type == XmlParserTypes.Attribute)


Attributes.TryGetValue(name, out target);


else if (type == XmlParserTypes.InnerElement)


Children.TryGetValue(name, out target);


else if (type == XmlParserTypes.Auto)


    {


if (Attributes.TryGetValue(name, out target))


return target;


else if (Children.TryGetValue(name, out target))


return target;


}


else


throw new NotSupportedException("error enum");


 


return target;


}


#endregion


}


}

[/code]

 

代码中还有一些属性和方法我没做解释,但只要你看完代码后会了解的。这里提一个要求,如果您需要转载的话,请注明出处,费不了一分钟的,谢谢了!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐