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

App.Config 文件中的自定义对象

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.59/5 (21投票s)

2004年4月18日

6分钟阅读

viewsIcon

170613

downloadIcon

3620

在 App.Config 文件中定义自定义对象,并通过 IConfigurationSectionHandler 接口检索它们。

引言

本文介绍如何在 App.Config 文件中实现自定义对象,允许数据以 XML 格式存储,并轻松反序列化为自定义对象。

背景

假定读者对 System.Xml.Serialization 命名空间中的 XML 序列化有相当的了解。要使一个对象可用于 App.Config 文件,它必须是 Serializable 的。请参阅 MS 帮助文件中的 Introducing XML Serialization

使用代码

在此示例中,我创建了两个将在 App.Config 文件中配置的类。第一个是一个简单的类,用于说明如何为反序列化配置一个简单的类,第二个更复杂,还涉及一个集合类。我在示例代码中包含了第二个类和相关类,但只描述了第一个类。额外的复杂性与自定义配置处理无关,更多地与 XML 序列化有关,因此我将把它留给读者结合 XML 序列化文档阅读代码以获得更好的理解。

对于第一个示例,我创建了一个名为 ProgramInfo 的类,其中包含三个公共属性。

  • 名称
  • 语言
  • 信号强度

前两个实现为公共字符串,第三个使用属性访问器。这是更好的做法,我包含了前两个是为了说明序列化是如何工作的。只要元素名称相同,它就会被找到,而无论你如何实现属性。

App.Config 文件中第一个要注意的代码是告诉 CLR 我们有一个自定义配置节的元素。它位于 configSections 的子元素 section 中,是 configuration 文档元素的直接子元素。它应该如下所示。

<configuration>
 <configSections>
   <section name="ProgramInfo" 
     type="ConfigSectionHandler.ConfigSectionHandler, ConfigSectionHandler" />
 </configSections>
</configuration>

最主要的元素是 section 元素。这表明将有一个与 name 属性中包含的名称相同的元素,在本例中是 ProgramInfotype 属性指定 CLR 应该实例化 ConfigSectionHandler 程序集中的 ConfigSectionHandler.ConfigSectionHandler 类的一个实例。语法与 Type 类使用的语法相同。

在我们查看 ConfigSectionHandler 类之前,让我们看看配置文件中更靠下的 ProgramInfo 部分。

<ProgramInfo type="ConfigSectionObjects.ProgramInfo, ConfigSectionObjects">
  <Name>ConfigSectionDemo</Name>
  <Language>C#</Language>
  <Level>Intermediate</Level>
</ProgramInfo>

这是直接的 XML,但请注意 type 属性。这再次使用相同的语法来描述应该实例化哪个对象来包含此 XmlNode 中的信息。这些信息不是配置系统的一部分。此完整节点,其中包含的所有信息都供我们即将使用的 ConfigSectionHandler 使用。

让我们看一下 ProjectInfo 类的简化版本。代码如下。

namespace ConfigSectionObjects
{
  public class ProgramInfo
  public ProgramInfo()    {
  }

  public string Name;
  public string Language;

  private string _Level;
  public string Level 
  {
    get {return _Level;}
    set {_Level=value;}
  }
}

一个相当简单的类。我以两种不同的方式实现了三个属性,前两个使用公共变量,这并不是处理属性访问的好方法。最后一个属性使用访问器方法。

注意:命名空间和类名的组合为我们提供了 ConfigSectionObjects.ProgramInfo,这是我们在自定义配置节的 ProgramInfo 元素中的 type 属性中指定的值。

类名与自定义节中的元素名相同。

两个公共变量和一个公共属性直接映射到 ProgramInfo 元素的子元素。所有这些值都直接映射。不一定非得是这样,有一些方法可以将元素名映射到不同的属性,或者让属性映射到属性。这些将在后面简要讨论。

此时,我们已经在 App.Config 文件中指定了所有需要的信息,并且还定义了将从 App.Config 文件反序列化的类。现在,我们需要使用一些代码来处理反序列化。为此,我们需要使用 ConfigurationSettings 类的 GetConfig 静态方法,并告诉它我们想要哪个 section。此方法的返回类型是 Object,因此我们需要将其转换回我们所需的类型。代码如下。

ConfigSectionObjects.ProgramInfo pi=
  (ConfigSectionObjects.ProgramInfo)ConfigurationSettings.GetConfig("ProgramInfo");

我们声明一个我们将要反序列化的类型的变量,并转换该方法返回的对象。现在,变量 piConfigSectionObjects.ProgramInfo 的一个有效实例,因此我们可以像这样访问它的属性。

Console.WriteLine ("Program: {0}", pi.Name);
Console.WriteLine ("Language: {0}", pi.Language);
Console.WriteLine ("Level: {0}", pi.Level);

现在,值得看一下实际的 ConfigSectionHandler 代码。它非常简单。它是一个名为 Create 的方法,位于实现 IConfigurationSectionHandler 接口的类中。当我们上面指定的代码行遇到 GetConfig 调用时,就会调用此方法。传递的对象由 CLR 传递。MSDN 文档如下所述。

  • parent:相应父配置节中的配置设置。
  • configContext:当 Create 从 ASP.NET 配置系统调用时,为 HttpConfigurationContext。否则,此参数保留,并且为空引用(在 Visual Basic 中为 Nothing)。
  • section:包含配置文件的配置信息的 XmlNode。提供对配置节 XML 内容的直接访问。

我们只关心 section。这是我们在 App.Config 文件中定义的 XmlNode。因此,我们需要执行几个步骤。

  • 创建一个 XPathNavigator 对象。
  • 使用 XPathNavigator 获取我们在 section 中定义的 type 属性。
  • 基于属性值创建一个 Type 对象。
  • 基于 type 创建一个 XmlSerializer
  • 使用我们的自定义 section 创建一个 XmlNodeReader
  • 调用 Deserialize 并返回反序列化的对象。

代码如下。

XPathNavigator xNav=section.CreateNavigator();
string typeOfObject=(string) xNav.Evaluate("string(@type)");
Type t=Type.GetType(typeOfObject);
XmlSerializer ser=new XmlSerializer(t);
XmlNodeReader xNodeReader=new XmlNodeReader(section);
return ser.Deserialize(xNodeReader);

复杂序列化

可以进行更复杂的序列化。例如,在 ProgramInfo 示例中,如果我们想将 ProgramInfo 类中的 name 元素重命名为 Description,但又不想修改 App.Config 文件,那么只需在变量声明上方添加一个属性即可。如下所示。

[XmlElement("Name")]
public string Description;

这告诉反序列化器,当在配置 section 中找到 Name 元素时,其值应放置在 ProgramInfo 类的 Description 属性中。

或者,如果你希望 LevelProgramInfo 的一个 attribute 而不是一个 element,那么,App.Config 文件可以更改为删除 Level 元素,并将其添加为 attribute,然后将 ProgramInfo 类更改为读取。

[XmlAttribute("Level2")]
public string Level2;

再次参考示例代码中的 ServerConfig 部分以获取更复杂的示例。

关注点

我发现我在定义 type 属性时犯了很多错误,我会正确地写程序集名称,但忘记用命名空间完全限定类名。发生这种情况时,代码将抛出 System.Configuration.ConfigurationException

我还发现有关配置文件方面的文档非常含糊。

历史

  • 2004 年 4 月 20 日 - 版本 1.0.1 - 修正了关于将 level 元素更改为 attribute 的拼写错误。
  • 2004 年 4 月 13 日 - 版本 1.0.0。
© . All rights reserved.