具有一组参数的多个实例的 .NET 配置
.NET configuration with multiple instances of a set of parameters
我有一个可以连接到多个服务器的应用程序。直到运行时才知道实际的服务器数量,并且可能每天都在变化。需要几个实际参数才能完整定义服务器。
我正在尝试使用 .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 用于诊断问题。它们会消失 when/if 它有效。):
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.
如果我删除第二台服务器,只留下华盛顿,它 "works" 会产生以下输出:
System.Configuration.Configuration
System.Configuration.ConfigurationSectionGroup: userSettings
System.Configuration.ConfigurationSectionCollection: 1
System.Configuration.DefaultSection
我尝试了主题的其他变体(嵌套的 sectionGroups 等),但没有找到解决方案。我发现的各种教程示例似乎要我为每个服务器创建一个单独的 class。这显然是不切实际的,而且应该是不必要的,因为所有服务器生而平等。
问题:
- System.Configuration是否支持相关设置集合的概念(如结构数组)?
- 如果是这样..怎么办?
- 如果不是..是否有另一种方法来存储支持此概念的持久配置信息,或者我是否必须自己动手?
进度报告
根据 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
属性更改为 class ystem.Configuration.DefaultSection
DefaultSection
成功读取配置信息(我认为,在至少它不会抱怨。)但它不会公开任何方式来访问它读取的信息!
因此我需要使用其他类型的 *Section
class。 Microsoft 提供了从 ConfigurationSection
基础 class 派生的 95 classes,但除了 DefaultSection
和 ClientSettingsSection
之外的所有这些似乎都是针对特殊情况(url,数据库连接字符串、日期和时间等...) ClientSettingsSection
甚至不会阅读服务器部分——抱怨 <add/>
不是有效的嵌套元素。
所以,底线是我将不得不编写一个自定义 ConfigurationSection
来处理这些设置。如果我让它工作,我会添加一个最终解决方案的答案(除非有人先提供更好的答案。)
没有使用过自定义配置部分,但从纯逻辑的角度来看,这不是更合适吗:
<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"绝对不是正确的类型。
结论
- 如果不创建自定义 属性 类。
,则无法支持 属性 集的集合
- 支持创建自定义 属性 类 非常好。
- 创建自定义 属性 类 的文档很糟糕,但是我终于找到了一个不错的 overview document 帮助我找到答案的文档。
配置文件
我最终得到的 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>
比我预期(或想要)多了一层嵌套。那是因为presidents
是一个ConfigurationElementCollection
,而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"]; } }
}
我有一个可以连接到多个服务器的应用程序。直到运行时才知道实际的服务器数量,并且可能每天都在变化。需要几个实际参数才能完整定义服务器。
我正在尝试使用 .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 用于诊断问题。它们会消失 when/if 它有效。):
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.
如果我删除第二台服务器,只留下华盛顿,它 "works" 会产生以下输出:
System.Configuration.Configuration
System.Configuration.ConfigurationSectionGroup: userSettings
System.Configuration.ConfigurationSectionCollection: 1
System.Configuration.DefaultSection
我尝试了主题的其他变体(嵌套的 sectionGroups 等),但没有找到解决方案。我发现的各种教程示例似乎要我为每个服务器创建一个单独的 class。这显然是不切实际的,而且应该是不必要的,因为所有服务器生而平等。
问题:
- System.Configuration是否支持相关设置集合的概念(如结构数组)?
- 如果是这样..怎么办?
- 如果不是..是否有另一种方法来存储支持此概念的持久配置信息,或者我是否必须自己动手?
进度报告
根据 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
属性更改为 class ystem.Configuration.DefaultSection
DefaultSection
成功读取配置信息(我认为,在至少它不会抱怨。)但它不会公开任何方式来访问它读取的信息!
因此我需要使用其他类型的 *Section
class。 Microsoft 提供了从 ConfigurationSection
基础 class 派生的 95 classes,但除了 DefaultSection
和 ClientSettingsSection
之外的所有这些似乎都是针对特殊情况(url,数据库连接字符串、日期和时间等...) ClientSettingsSection
甚至不会阅读服务器部分——抱怨 <add/>
不是有效的嵌套元素。
所以,底线是我将不得不编写一个自定义 ConfigurationSection
来处理这些设置。如果我让它工作,我会添加一个最终解决方案的答案(除非有人先提供更好的答案。)
没有使用过自定义配置部分,但从纯逻辑的角度来看,这不是更合适吗:
<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"绝对不是正确的类型。
结论
- 如果不创建自定义 属性 类。 ,则无法支持 属性 集的集合
- 支持创建自定义 属性 类 非常好。
- 创建自定义 属性 类 的文档很糟糕,但是我终于找到了一个不错的 overview document 帮助我找到答案的文档。
配置文件
我最终得到的 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>
比我预期(或想要)多了一层嵌套。那是因为presidents
是一个ConfigurationElementCollection
,而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"]; } }
}