如何从app.config文件中读取对象?

7

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

我有一个基本的类叫做“Person”,其中包含三个自动属性:(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
你在哪里定义自定义配置部分?https://msdn.microsoft.com/zh-cn/library/2tw134k3.aspx - David
@David 那就是我缺少的东西吗?我之前有一个,基于已弃用的 ConfigurationSettings,最终放弃了它。我现在感到非常困惑。我会尝试制作一个新的。 - randomraccoon
2个回答

10
给定一个名为 Person 的 POCO 类:
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

首先,您需要创建一个类,该类继承自System.Configuration.ConfigurationElement,就像这样:

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的类,如下所示:

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();

3
哇,谢谢!我完全不知道我需要那个PersonElement类。对于我来说,这个学习曲线相当陡峭,所以我非常感激您的帮助。 - randomraccoon
如果我们在“People”下有多个人,例如多个“Person”对象,如何获取值?我们该如何从代码中检索它?感谢任何帮助。 - Muni

2
你的实现有几个问题,它们是:
  1. <configSections> 元素必须是你的 App.config 文件中的第一个元素
  2. 配置节处理程序(在 section 元素的 type 属性中描述的类型)必须继承自ConfigurationSection
  3. 完全限定引用的类型
<configSections> 元素必须是你的 App.config 文件中的第一个元素 抛出的 ConfigurationErrorsException 包含以下详细信息:

每个配置文件只允许一个 <configSections> 元素,如果存在,则必须是根 <configuration> 元素的第一个子元素。

这意味着你必须将 <configSections> 元素移到文件顶部。我想这是为了使处理配置文件的代码可以在读取每个配置文件节之前加载适当的处理程序。 配置节处理程序(在 section 元素的 type 属性中描述的类型)必须继承自 ConfigurationSection 不幸的是,配置系统的工作方式意味着你不能只是放一个 POCO 就能自动完成所有设置。在 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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接