技术开发 频道

自动重载配置文件读取类:代码支招

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

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

private static XmlParser xml = null;static Default()
{    var path = HttpContext.Current.Server.MapPath(".\\TestData.xml");    
xml = XmlParser.Load(path, new string[] { "orange" });
}

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

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

1:  <?xml version="1.0" encoding="utf-8"?>   2:  <apple phone="true">
3:    <items>  
4:      <orange id="1">  
5:        <name>橘子1</name>  
6:      </orange>  
7:      <orange id="o2">  
8:        <name>橘子2</name>  
9:      </orange>  
10:      <orange id="o3">  
11:        <name>橘子3</name>  
12:      </orange>  
13:      <orange id="o4">  
14:        <name>橘子4</name>  
15:      </orange>
16:    </items>  
17:    <id>a1</id>  
18:    <name>苹果</name>
19:  </apple>

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

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

  
1:  string a = xml.GetString("name");
  
2:  string b = xml.GetString("phone");

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

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

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

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

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

还有一些需求是不指定子级,在运行时动态分析。比如在 apple 节点下直接添加 orange 节点,此时如果想要获取 orange 的节点信息,你可以求助于InnerParsers 属性,该属性包含了 apple 下的所有子节点,当然也就有这个非正统的 orange 节点。顺带提一句,Items 属性中只会包含在 items 子节点下的所有符合 orange 名称的节点。orange 通过 Load 方法第二个参数指定,逐级递归时依然有效。

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

using System;
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
    }
}
0