带有一组参数的多个实例的. net配置

本文关键字:实例 配置 net 参数 一组 | 更新日期: 2023-09-27 18:01:46

我有一个可以连接到多个服务器的应用程序。服务器的实际数量直到运行时才知道,并且可能每天都在变化。需要几个实际参数才能完全定义一个服务器。

我正在尝试使用。net支持应用程序配置。

配置文件看起来像这样:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <sectionGroup
           name="userSettings"
            type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="server"
                type="System.Configuration.SingleTagSectionHandler"
                allowExeDefinition="MachineToLocalUser"
                requirePermission="false" />
        </sectionGroup>
    </configSections>
    <userSettings>
        <server name="washington">
            <add name="host" value="abc.presidents.com"/>
            <add name="port" value="1414"/>
            <add name="credentials" value="george"/>
        </server>
        <server name="adams">
            <add name="host" value="def.presidents.com"/>
            <add name="port" value="1419"/>
            <add name="credentials" value="john"/>
        </server>
        <!--insert more server definitions here -->
    </userSettings>
</configuration>

我试着用这样的代码来阅读这个(WriteLines是用来诊断问题的)。如果它起作用,它们就会消失。

try
{
    var exeConfiguration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
    Console.WriteLine(exeConfiguration);
    var userSettings = exeConfiguration.GetSectionGroup("userSettings");
    Console.WriteLine("{0}: {1}", userSettings, userSettings.Name);
    var sections = userSettings.Sections;
    Console.WriteLine("{0}: {1}", sections, sections.Count);
    foreach (ConfigurationSection section in sections)
    {
        Console.WriteLine("{0}", section);
        // todo Here's where we capture information about a server
    }
}
catch (System.Exception ex)
{
    Console.WriteLine("Exception: {0}", ex.Message);
}

这会从foreach抛出一个异常,并产生以下输出:

    System.Configuration.Configuration
    System.Configuration.UserSettingsGroup: userSettings
    System.Configuration.ConfigurationSectionCollection: 1
    Exception: Sections must only appear once per config file.  See the help topic <location> for exceptions.

如果我删除第二台服务器,只留下washington,它"工作"产生以下输出:

System.Configuration.Configuration
System.Configuration.ConfigurationSectionGroup: userSettings
System.Configuration.ConfigurationSectionCollection: 1
System.Configuration.DefaultSection

我已经尝试了主题的其他变化(嵌套的sectionGroups等)没有找到解决方案。我发现的各种教程示例似乎都希望我为每个服务器创建一个单独的类。这显然是不切实际的,而且应该是不必要的,因为所有服务器都是平等创建的。

问题:

  • 系统。配置支持相关设置集合的概念(如结构数组)?
  • 如果是这样. .如何?
  • 如果不是. .是否有另一种方法来存储支持此概念的持久配置信息,或者我必须自己动手?
<标题>进展报告

根据Robert McKee的部分回答,我对xml进行了如下修改:

            <section name="servers"
                type="System.Configuration.DefaultSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"

<servers>
    <add name="washington" host="abc.presidents.com" port="1414" credentials="george"/>
    <add name="adams" host="def.presidents.com" port="1419" credentials="john"/>
    <!--insert more server definitions here -->
</servers>

这是一个改进,但有一个致命的问题。正如您所看到的,我更改了section元素的type属性,将类命名为ystem.Configuration.DefaultSection, DefaultSection成功读取配置信息(我认为,至少它没有抱怨),但它没有暴露任何访问它所读取的信息的方法!

因此我需要使用其他类型的*Section类。微软提供了95个从ConfigurationSection基类派生的类,但除了DefaultSectionClientSettingsSection之外,所有这些类似乎都针对特殊情况(url、数据库连接字符串、日期和时间等)。ClientSettingsSection甚至不会读取服务器部分——抱怨<add/>不是一个有效的嵌套元素。

所以,底线是我将不得不写一个自定义ConfigurationSection来处理这些设置。如果我让它工作,我会添加一个答案与最终解决方案(除非有人提供更好的答案先。)

带有一组参数的多个实例的. net配置

结论

  • 如果不创建自定义属性类,则不支持属性集集合。
  • 支持创建自定义属性类是非常好的。
  • 创建自定义属性类的文档是糟糕的,但是我终于找到了一个体面的概述文档,帮助我找到了答案。

配置文件

我最终得到的app.config文件是:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <sectionGroup name="userSettings" 
                      type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="executiveBranch" 
                     type="PropProto.Properties.ExecutiveBranchSection, PropProto, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 
                     allowExeDefinition="MachineToLocalUser" 
                     requirePermission="false" />
        </sectionGroup>
    </configSections>
    <userSettings>
        <executiveBranch>
            <presidents>
                <president key="first"
                           name="George Washington"
                           legacy="The Fother of Our County"
                           />
                <president key="honestAbe"
                           name="Abraham Lincoln"
                           legacy="Freed the Slaves"
                           />
                <president key="who"
                           name="Chester Arthur"
                           />
                <president key="dubya"
                           name="George W. Bush"
                           legacy="Mission Accomplished!!!"
                           />
                <president key="barack"
                           name="Barack Obama"
                           legacy="Affordable Health Care"
                           />
            </presidents>
        </executiveBranch>
    </userSettings>
</configuration>

比我预期的(或想要的)多了一个嵌套层。这是因为presidentsConfigurationElementCollection,而userSettings不能直接包含ConfigurationElementCollection,所以我不得不引入executiveBranch

使用配置

读取这些设置的代码是:
var exeConfiguration = ConfigurationManager.OpenExeConfiguration(
      ConfigurationUserLevel.PerUserRoamingAndLocal);
var userSettings = exeConfiguration.GetSectionGroup("userSettings");
var executiveBranch = (ExecutiveBranchSection)userSettings.Sections.Get(
        ExecutiveBranchSection.Tag);
var presidents = executiveBranch.Presidents;
foreach (President president in presidents)
{
    Console.WriteLine("{0}: {1}", president.Name, president.Legacy);
}

自定义属性类

和使这一切工作的自定义类:

public class ExecutiveBranchSection : ConfigurationSection
{
    public const string Tag = "executiveBranch";
    [ConfigurationProperty(PresidentCollection.Tag)]
    public PresidentCollection Presidents { get { return (PresidentCollection)base[PresidentCollection.Tag]; } }
}
[ConfigurationCollection(typeof(President),
    CollectionType = ConfigurationElementCollectionType.BasicMap,
    AddItemName = President.Tag)]
public class PresidentCollection : ConfigurationElementCollection
{
    public const string Tag = "presidents";
    protected override string ElementName { get { return President.Tag; } }
    public President this[int index]
    {
        get { return (President)base.BaseGet(index); }
        set
        {
            if (base.BaseGet(index) != null)
            {
                base.BaseRemoveAt(index);
            }
            base.BaseAdd(index, value);
        }
    }
    new public President this[string name] { get { return (President)base.BaseGet(name); } }
    protected override ConfigurationElement CreateNewElement()
    {
        return new President();
    }
    protected override object GetElementKey(ConfigurationElement element)
    {
        return (element as President).Key;
    }
}
public class President : ConfigurationElement
{
    public const string Tag = "president";
    [ConfigurationProperty("key", IsRequired = true)]
    public string Key { get { return (string)base["key"]; } }
    [ConfigurationProperty("name", IsRequired = true)]
    public string Name { get { return (string)base["name"]; } }
    [ConfigurationProperty("legacy", IsRequired = false)]
    public string Legacy { get { return (string)base["legacy"]; } }
}

没有使用自定义配置部分,但从纯逻辑的角度来看,这不是更合适吗:

<userSettings>
    <servers>
        <add name="washington" host="abc.presidents.com" port="1414" credentials="george"/>
        <add name="adams" host="def.presidents.com" port="1419" credentials="john"/>
        <!--insert more server definitions here -->
    </servers>
    <!-- insert more user settings here -->
</userSettings>

你可以用其他的方法,但是如果你想要一个"server"的集合,那么它必须在一个分组元素中,比如"servers"。不能在父元素中只包含多个"server",而父元素还可以包含其他类型的子元素。组内的标签几乎都是"add"

在任何情况下"System.Configuration. "SingleTagSectionHandler"绝对不是正确的类型。