如何从 app.config 中读取一个对象?

How to read an object from app.config?

这不是一个新问题,但我已经研究了两天,我能找到的所有答案都已过时或无用。我想做的是将一个对象放入App.config,然后在程序启动时加载它。

我有一个名为 "Person" 的基本 class,它具有三个自动属性:(string) FirstName、(string) LastName 和 (int) Age。这是我的 App.config 文件:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/></startup>
  <configSections>
    <sectionGroup name="People">
      <section 
        name="Person" 
        type="Person.Person"
      />
    </sectionGroup>
  </configSections>
  <People>
    <Person>
      <Person type="Person.Person, Person">
        <FirstName>Jimmy</FirstName>
        <LastName>Dean</LastName>
        <Age>2</Age>
      </Person>
    </Person>
  </People>
</configuration>

这是我的程序:

using System;
using System.Configuration;

namespace AppConfigTest
{
    class AppConfigTester
    {
        public static void Main(string[] args)
        {
            var guy = (Person.Person) ConfigurationManager.GetSection("People/Person");
            Console.WriteLine(guy.FirstName);
            Console.WriteLine(guy.LastName);
            Console.WriteLine(guy.Age);
        }
    }
}

目前它因 ConfigurationErrorsException 而崩溃。任何帮助将不胜感激。令我难以置信的是,这太难了,而 App.config 本来应该让做这种事情变得更容易。

您的实施存在一些问题,它们是:

  1. <configSections> 元素必须是 App.config 文件中的第一个元素
  2. 配置部分处理程序(在 section 元素的 type 属性中描述的类型)必须继承自 ConfigurationSection.
  3. 完全限定您所指的类型

<configSections> 元素必须是 App.config 文件中的第一个元素

抛出的 ConfigurationErrorsException 包含以下详细信息:

Only one <configSections> element allowed per config file and if present must be the first child of the root <configuration> element.

这表明您必须将 <configSections> 元素移动到文件顶部。我想这是为了让处理配置文件的代码可以在读取配置文件的每个部分之前为每个部分加载适当的处理程序

配置部分处理程序(在 section 元素的 type 属性中描述的类型)必须继承自 ConfigurationSection

不幸的是,配置系统的工作方式意味着您不能只删除 POCO in and have it take care of wiring it all up for you. There is a tutorial for creating a custom configuration section on MSDN

完全限定您所指的类型

我不确定这是否真的给您带来了问题,但准确地说避免它造成问题并没有什么坏处。以下:

<section name="Person" type="Person.Person" />

可能有歧义。假设您的项目编译为一个名为 "MyProjectThatContainsPerson" 的 DLL/EXE,您应该考虑将其更改为:

<section name="Person" type="Person.Person, MyProjectThatContainsPerson" /> 

这不仅让配置系统清楚地知道类型的名称是什么 (Person.Person),而且还清楚它应该尝试从哪个程序集加载它 (MyProjectThatContainsPerson)。

使用内置部分处理程序的示例自定义部分

如果您想添加一个具有自定义名称(例如 "MySection")但其他方面与 appSettings 相同的配置部分,您可以:

<configSections>
    <section name="MySection" 
             type="System.Configuration.NameValueSectionHandler, system, 
                   Version=1.0.3300.0, Culture=neutral, 
                   PublicKeyToken=b77a5c561934e089, Custom=null" />
</configSections>
<MySection>
    <add key="MySetting" value="MyValue" />
</MySection>

给定一个人 POCO class:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

首先,您需要创建一个继承 System.Configuration.ConfigurationElement 的 class,如下所示:

public class PersonElement : ConfigurationElement
{
    public string InnerText { get; private set; }

    protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey)
    {
        InnerText = reader.ReadElementContentAsString();
    }
}

这是必需的,这样您就可以拥有像 <FirstName>Jimmy</FirstName> 这样带有内部文本的配置元素。

接下来你需要一个像这样继承System.Configuration.ConfigurationSection的class:

public class PersonSection : ConfigurationSection
{
    [ConfigurationProperty("FirstName")]
    public PersonElement FirstName
    {
        get { return this["FirstName"] as PersonElement; }
        set { this["FirstName"] = value; }
    }

    [ConfigurationProperty("LastName")]
    public PersonElement LastName
    {
        get { return this["LastName"] as PersonElement; }
        set { this["LastName"] = value; }
    }

    [ConfigurationProperty("Age")]
    public PersonElement Age
    {
        get { return this["Age"] as PersonElement; }
        set { this["Age"] = value; }
    }

    public Person CreatePersonFromConfig()
    {
        return new Person()
        {
            FirstName = this.FirstName.InnerText,
            LastName = this.LastName.InnerText,
            Age = Convert.ToInt32(this.Age.InnerText)
        };
    }
}

您的 app.config 应如下所示:

<configuration>
    <configSections>
        <sectionGroup name="People">
            <section name="Person" type="Example.PersonSection, Example" />
        </sectionGroup>
    </configSections>
    <People>
        <Person>
            <FirstName>Jimmy</FirstName>
            <LastName>Dean</LastName>
            <Age>2</Age>
        </Person>
    </People>
</configuration>

最后在您的代码中的某处执行此操作:

PersonSection config = (PersonSection)ConfigurationManager.GetSection("People/Person");
Person guy = config.CreatePersonFromConfig();