65.9K
CodeProject 正在变化。 阅读更多。
Home

简单的 XML 配置文件读/写器

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.25/5 (5投票s)

2008年8月22日

CPOL

3分钟阅读

viewsIcon

47928

downloadIcon

607

一个非常容易使用的 .NET 配置文件读/写器。

背景

我经常看到开发人员实现他们自己的 XML 配置读/写器版本,这可能需要时间和精力进行开发和维护。 尽管 .NET 2.0 提供了一个配置库,但我还没有看到它被广泛使用。

配置是没有人有一个明确和一致的方法的一个方面。 根据我的经验,我看到大多数开发人员实现他们自己的代码版本来存储/访问配置(这可能在数据库、XML 文件、INI 文件或注册表中)。

在这个示例中,我提出了一个非常基本的 XML 配置文件,整个示例应用程序都基于它,并且我相信它会非常容易使用。 您可以随时扩展配置文件/示例应用程序以满足您的需求,或者使用数据库而不是 XML 等,但我认为这个示例应该足以让您入门,并且将帮助您更容易地维护访问配置文件的代码。

简单的想法

嗯,这不是一个开创性的示例应用程序,但就像我提到的,它可能有助于开发人员理解和重用组件。 此外,由于此应用程序使用反射、自定义属性和 LINQ,我想这将是一个很好的示例练习,以了解初学者级别的概念。

所以,在我们开始之前,这是示例配置文件的样子

<Configuration> 
  <Module Name="Application-A">
    <Section Name="Section-A">
      <Entry Name="RefreshRate">11</Entry>
    </Section>
  </Module>
  <Module Name="Application-B">
    <Section Name="Section-B">
      <Entry Name="LogFile">SomeLogFile.Log</Entry>
    </Section>
  </Module>
</Configuration>

配置存储在ModuleSection 内,作为一个 Entry 标签。

我将很快描述如何加载和保存文件,但在此之前,让我们讨论如何检索和保存配置变量。

XML 标签,即 ModuleSectionEntry,被映射到枚举值,因此它们易于读取和写入,类似于属性字段。 这是通过我们附加到每个枚举值的自定义属性来实现的。 自定义属性采用应用程序、Section 标签和 Entry 标签的各自名称,它们与该名称相关; 例如,下面示例中的 RefreshRate 枚举值。

public enum Confiugration
{
    [CustomConfig(ApplicationName = "Application-A", 
     SectionName = "Section-A", 
     EntryName = "RefreshRate", 
     DefaultValue = "5")]
    RefreshRate,
    [CustomConfig(ApplicationName = "Application-B", 
     SectionName = "Section-B", 
     EntryName = "LogFile")]
    LogFile
};

这允许我们使用以下代码读取配置

configHelper.GetConfigEntry(Configuration.RefreshRate);

类似地,您可以使用以下代码保存配置值

configHelper.SetConfigEntry(Confiugration.RefreshRate, "11");

这是所需的五行自定义属性代码

[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public sealed class CustomConfigAttribute : Attribute
{
    public String ApplicationName { get; set; }
    public String SectionName { get; set; }
    public String EntryName { get; set; }
    public String DefaultValue { get; set; }
}

简而言之,您不必实现任何代码来读取或写入变量; 您所要做的就是为您的配置创建一个枚举,并填写附加到枚举值的适当属性值,并让 CustomConfigHelper 类为您读取和写入配置条目。

我确信您一定很清楚 CustomConfigHelper 类提供了读取附加到枚举值的属性的函数,并根据需要读取/更新配置文件。

为了读取附加到枚举的自定义属性,CustomConfigHelper 类使用反射来处理您传递的枚举。 例如,对于 GetConfigEntry 函数,我们会这样做

public String GetConfigEntry(Enum enumValue)
{  
    Type type = enumValue.GetType();
    FieldInfo info = type.GetField(enumValue.ToString());
    var customAttribute = Attribute.GetCustomAttribute(info, 
        typeof(CustomConfigAttribute)) as CustomConfigAttribute;

一旦检索到自定义属性,我们就可以使用 LINQ 从 xml 文件中检索适当的条目。 为了加载 xml 文件,我们使用 XElement.Load 函数

if (File.Exists(fileName))
{
    rootNode = XElement.Load(fileName); 
    var item = from applicationNode in rootNode.Elements("Module")
               where (String)applicationNode.Attribute("Name") == 
                      customAttribute.ApplicationName
               from sectionNode in applicationNode.Elements("Section")
               where (String)sectionNode.Attribute("Name") == 
                      customAttribute.SectionName
               from entryNode in sectionNode.Elements("Entry")
               where (String)entryNode.Attribute("Name") == 
                      customAttribute.EntryName
               select entryNode.FirstNode;

    if (item.Any())
    {
        return item.First().ToString();
    }
}

类似地,我们也可以保存配置

var item = from applicationNode in rootNode.Elements("Module")
                       where (String) applicationNode.Attribute("Name") == 
                              customAttribute.ApplicationName
                       from sectionNode in applicationNode.Elements("Section")
                       where (String) sectionNode.Attribute("Name") == 
                              customAttribute.SectionName
                       from entryNode in sectionNode.Elements("Entry")
                       where (String) entryNode.Attribute("Name") == 
                              customAttribute.EntryName
                       select entryNode;

if(!item.Any())
{
    return;
}

item.First().Value = entryValue;

关注点

这是 CustomConfigHelper 类的完整代码,它可以为您完成神奇的事情

public class CustomConfigHelper
{
    private static XElement rootNode;

    private CustomConfigHelper(){}
    private static String configFileName { get; set; }

    public CustomConfigHelper(String fileName)
    {
        if (File.Exists(fileName))
        {
            rootNode = XElement.Load(fileName);
            configFileName = fileName;
        }
    }

    private static void SaveConfigFile(String fileName)
    {
        rootNode.Save(fileName);
    }

    public String GetConfigEntry(Enum enumValue)
    {
        Type type = enumValue.GetType();
        FieldInfo info = type.GetField(enumValue.ToString());
        var customAttribute = Attribute.GetCustomAttribute(info, 
            typeof(CustomConfigAttribute)) as CustomConfigAttribute;

        return customAttribute == null ? String.Empty : 
               GetConfigEntry(customAttribute);
    }

    public void SetConfigEntry(Enum enumValue, String entryValue)
    {
        if(entryValue == null)
        {
            entryValue = String.Empty;
        }

        Type type = enumValue.GetType();
        FieldInfo info = type.GetField(enumValue.ToString());
        var customAttribute = Attribute.GetCustomAttribute(info, 
            typeof(CustomConfigAttribute)) as CustomConfigAttribute;
        SetConfigEntry(customAttribute, entryValue);
    }

    private static String GetConfigEntry(CustomConfigAttribute attribute)
    {
        if (rootNode == null)
        {
            return attribute.DefaultValue ?? String.Empty;
        }

        var item = from applicationNode in rootNode.Elements("Module")
                   where (String)applicationNode.Attribute("Name") == 
                          attribute.ApplicationName
                   from sectionNode in applicationNode.Elements("Section")
                   where (String)sectionNode.Attribute("Name") == 
                          attribute.SectionName
                   from entryNode in sectionNode.Elements("Entry")
                   where (String)entryNode.Attribute("Name") == 
                          attribute.EntryName
                   select entryNode.FirstNode;

        if (item.Any())
        {
            return item.First().ToString();
        }

        return attribute.DefaultValue ?? String.Empty;
    }

    private static void SetConfigEntry(CustomConfigAttribute attribute, 
                                       String entryValue)
    {
        if (rootNode == null)
        {
            return;
        }

        var item = from applicationNode in rootNode.Elements("Module")
                   where (String) applicationNode.Attribute("Name") == 
                          attribute.ApplicationName
                   from sectionNode in applicationNode.Elements("Section")
                   where (String) sectionNode.Attribute("Name") == 
                          attribute.SectionName
                   from entryNode in sectionNode.Elements("Entry")
                   where (String) entryNode.Attribute("Name") == 
                          attribute.EntryName
                   select entryNode;

        if(!item.Any())
        {
            return;
        }

        item.First().Value = entryValue;
        SaveConfigFile(configFileName);
    }
}

我希望这有帮助。 一如既往,欢迎提出意见和建议。 干杯。

历史

原始版本。

© . All rights reserved.