如何使用 ConfigurationElementCollection 实现 ConfigurationSection

我正试图在一个项目中实现一个自定义配置部分,我不断遇到我不理解的异常。我希望有人能填补这里的空白。

我的 App.config是这样的:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="ServicesSection" type="RT.Core.Config.ServicesConfigurationSectionHandler, RT.Core"/>
</configSections>
<ServicesSection type="RT.Core.Config.ServicesSection, RT.Core">
<Services>
<AddService Port="6996" ReportType="File" />
<AddService Port="7001" ReportType="Other" />
</Services>
</ServicesSection>
</configuration>

我有一个如下定义的 ServiceConfig元素:

public class ServiceConfig : ConfigurationElement
{
public ServiceConfig() {}


public ServiceConfig(int port, string reportType)
{
Port = port;
ReportType = reportType;
}


[ConfigurationProperty("Port", DefaultValue = 0, IsRequired = true, IsKey = true)]
public int Port
{
get { return (int) this["Port"]; }
set { this["Port"] = value; }
}


[ConfigurationProperty("ReportType", DefaultValue = "File", IsRequired = true, IsKey = false)]
public string ReportType
{
get { return (string) this["ReportType"]; }
set { this["ReportType"] = value; }
}
}

我的 ServiceCollection是这样定义的:

public class ServiceCollection : ConfigurationElementCollection
{
public ServiceCollection()
{
Console.WriteLine("ServiceCollection Constructor");
}


public ServiceConfig this[int index]
{
get { return (ServiceConfig)BaseGet(index); }
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}


public void Add(ServiceConfig serviceConfig)
{
BaseAdd(serviceConfig);
}


public void Clear()
{
BaseClear();
}


protected override ConfigurationElement CreateNewElement()
{
return new ServiceConfig();
}


protected override object GetElementKey(ConfigurationElement element)
{
return ((ServiceConfig) element).Port;
}


public void Remove(ServiceConfig serviceConfig)
{
BaseRemove(serviceConfig.Port);
}


public void RemoveAt(int index)
{
BaseRemoveAt(index);
}


public void Remove(string name)
{
BaseRemove(name);
}
}

我缺少的部分是如何处理。最初,我试图实现一个 IConfigurationSectionHandler,但发现了两个问题:

  1. 没成功
  2. 已经废弃了。

我现在完全不知道该怎么做,所以我可以读取我的数据从配置。任何帮助请!

165405 次浏览

尝试从 配置组继承。 Phil Haack 的 博客文章有一个例子。

确认,根据 IConfigurationSectionHandler的文档:

进去。NET Framework 2.0及以上版本,则必须从 ConfigurationSection 类派生,以实现相关的配置节处理程序。

前面的答案是正确的,但是我也会给你所有的代码。

你的 app.config 应该是这样的:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="ServicesSection" type="RT.Core.Config.ServiceConfigurationSection, RT.Core"/>
</configSections>
<ServicesSection>
<Services>
<add Port="6996" ReportType="File" />
<add Port="7001" ReportType="Other" />
</Services>
</ServicesSection>
</configuration>

你的 ServiceConfigServiceCollection课程保持不变。

你需要一个新的班级:

public class ServiceConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("Services", IsDefaultCollection = false)]
[ConfigurationCollection(typeof(ServiceCollection),
AddItemName = "add",
ClearItemsName = "clear",
RemoveItemName = "remove")]
public ServiceCollection Services
{
get
{
return (ServiceCollection)base["Services"];
}
}
}

这应该可以解决问题,你可以用:

ServiceConfigurationSection serviceConfigSection =
ConfigurationManager.GetSection("ServicesSection") as ServiceConfigurationSection;


ServiceConfig serviceConfig = serviceConfigSection.Services[0];

这是用于配置集合的通用代码:

public class GenericConfigurationElementCollection<T> :   ConfigurationElementCollection, IEnumerable<T> where T : ConfigurationElement, new()
{
List<T> _elements = new List<T>();


protected override ConfigurationElement CreateNewElement()
{
T newElement = new T();
_elements.Add(newElement);
return newElement;
}


protected override object GetElementKey(ConfigurationElement element)
{
return _elements.Find(e => e.Equals(element));
}


public new IEnumerator<T> GetEnumerator()
{
return _elements.GetEnumerator();
}
}

当你有了 GenericConfigurationElementCollection, 您可以在 config 部分中简单地使用它(这是 Dispatcher 中的一个示例) :

public class  DispatcherConfigurationSection: ConfigurationSection
{
[ConfigurationProperty("maxRetry", IsRequired = false, DefaultValue = 5)]
public int MaxRetry
{
get
{
return (int)this["maxRetry"];
}
set
{
this["maxRetry"] = value;
}
}


[ConfigurationProperty("eventsDispatches", IsRequired = true)]
[ConfigurationCollection(typeof(EventsDispatchConfigurationElement), AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")]
public GenericConfigurationElementCollection<EventsDispatchConfigurationElement> EventsDispatches
{
get { return (GenericConfigurationElementCollection<EventsDispatchConfigurationElement>)this["eventsDispatches"]; }
}
}

Config 元素是 Config Here:

public class EventsDispatchConfigurationElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get
{
return (string) this["name"];
}
set
{
this["name"] = value;
}
}
}

配置文件如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<dispatcherConfigurationSection>
<eventsDispatches>
<add name="Log" ></add>
<add name="Notification" ></add>
<add name="tester" ></add>
</eventsDispatches>
</dispatcherConfigurationSection>

希望能有帮助!

如果您正在寻找自定义配置节,如下所示

<CustomApplicationConfig>
<Credentials Username="itsme" Password="mypassword"/>
<PrimaryAgent Address="10.5.64.26" Port="3560"/>
<SecondaryAgent Address="10.5.64.7" Port="3570"/>
<Site Id="123" />
<Lanes>
<Lane Id="1" PointId="north" Direction="Entry"/>
<Lane Id="2" PointId="south" Direction="Exit"/>
</Lanes>
</CustomApplicationConfig>

然后您可以使用我的配置部分的实现,以便开始向您的项目添加 System.Configuration程序集引用

看看我使用的每个嵌套元素,第一个元素是带有两个属性的凭证,所以让我们先添加它

证书元素

public class CredentialsConfigElement : System.Configuration.ConfigurationElement
{
[ConfigurationProperty("Username")]
public string Username
{
get
{
return base["Username"] as string;
}
}


[ConfigurationProperty("Password")]
public string Password
{
get
{
return base["Password"] as string;
}
}
}

主要代理人和次要代理人

两者具有相同的属性,并且看起来像是一组服务器的地址,用于主服务器和故障转移,因此您只需要为这两者创建一个元素类,如下所示

public class ServerInfoConfigElement : ConfigurationElement
{
[ConfigurationProperty("Address")]
public string Address
{
get
{
return base["Address"] as string;
}
}


[ConfigurationProperty("Port")]
public int? Port
{
get
{
return base["Port"] as int?;
}
}
}

稍后我将解释如何在一个类中使用两个不同的元素,让我们跳过 SiteId,因为它们之间没有区别。您只需要创建一个与上面相同的类,只有一个属性。让我们看看如何实现 Lane 收集

它被分成两部分,首先你必须创建一个元素实现类,然后你必须创建集合元素类

LaneConfigElement

public class LaneConfigElement : ConfigurationElement
{
[ConfigurationProperty("Id")]
public string Id
{
get
{
return base["Id"] as string;
}
}


[ConfigurationProperty("PointId")]
public string PointId
{
get
{
return base["PointId"] as string;
}
}


[ConfigurationProperty("Direction")]
public Direction? Direction
{
get
{
return base["Direction"] as Direction?;
}
}
}


public enum Direction
{
Entry,
Exit
}

您可以注意到,LanElement的一个属性是枚举,如果您尝试在配置中使用枚举应用程序中未定义的任何其他值,将在启动时抛出 System.Configuration.ConfigurationErrorsException。好的,让我们继续讨论集合定义

[ConfigurationCollection(typeof(LaneConfigElement), AddItemName = "Lane", CollectionType = ConfigurationElementCollectionType.BasicMap)]
public class LaneConfigCollection : ConfigurationElementCollection
{
public LaneConfigElement this[int index]
{
get { return (LaneConfigElement)BaseGet(index); }
set
{
if (BaseGet(index) != null)
{
BaseRemoveAt(index);
}
BaseAdd(index, value);
}
}


public void Add(LaneConfigElement serviceConfig)
{
BaseAdd(serviceConfig);
}


public void Clear()
{
BaseClear();
}


protected override ConfigurationElement CreateNewElement()
{
return new LaneConfigElement();
}


protected override object GetElementKey(ConfigurationElement element)
{
return ((LaneConfigElement)element).Id;
}


public void Remove(LaneConfigElement serviceConfig)
{
BaseRemove(serviceConfig.Id);
}


public void RemoveAt(int index)
{
BaseRemoveAt(index);
}


public void Remove(String name)
{
BaseRemove(name);
}


}

你可以注意到,我已经设置了 AddItemName = "Lane"你可以选择任何你喜欢的集合条目项,我喜欢使用“添加”的默认一个,但我改变了它只是为了这篇文章。

现在我们所有的嵌套元素都已经实现了,我们应该将所有这些元素聚合到一个类中,该类必须实现 System.Configuration.ConfigurationSection

自定义应用程序配置部分

public class CustomApplicationConfigSection : System.Configuration.ConfigurationSection
{
private static readonly ILog log = LogManager.GetLogger(typeof(CustomApplicationConfigSection));
public const string SECTION_NAME = "CustomApplicationConfig";


[ConfigurationProperty("Credentials")]
public CredentialsConfigElement Credentials
{
get
{
return base["Credentials"] as CredentialsConfigElement;
}
}


[ConfigurationProperty("PrimaryAgent")]
public ServerInfoConfigElement PrimaryAgent
{
get
{
return base["PrimaryAgent"] as ServerInfoConfigElement;
}
}


[ConfigurationProperty("SecondaryAgent")]
public ServerInfoConfigElement SecondaryAgent
{
get
{
return base["SecondaryAgent"] as ServerInfoConfigElement;
}
}


[ConfigurationProperty("Site")]
public SiteConfigElement Site
{
get
{
return base["Site"] as SiteConfigElement;
}
}


[ConfigurationProperty("Lanes")]
public LaneConfigCollection Lanes
{
get { return base["Lanes"] as LaneConfigCollection; }
}
}

现在您可以看到,我们有两个名为 PrimaryAgentSecondaryAgent的属性都具有相同的类型,现在您可以很容易地理解为什么针对这两个元素我们只有一个实现类。

在你可以在 app.config (或者 web.config)中使用这个新发明的配置部分之前,你只需要告诉你的应用程序你已经发明了你自己的配置部分,并给予它一些尊重,为了这样做,你必须在 app.config 中添加以下代码行(可能就在根标记开始之后)。

<configSections>
<section name="CustomApplicationConfig" type="MyNameSpace.CustomApplicationConfigSection, MyAssemblyName" />
</configSections>

注意: MyAssemblyName 应该没有. dll。例如,如果汇编文件名是 myDll.dll,那么使用 myDll 代替 myDll.dll

在应用程序的任何位置使用以下代码行检索此配置

CustomApplicationConfigSection config = System.Configuration.ConfigurationManager.GetSection(CustomApplicationConfigSection.SECTION_NAME) as CustomApplicationConfigSection;

我希望上面的文章能够帮助您开始学习一些比较复杂的自定义配置部分。

快乐编码:)

编辑 要在 LaneConfigCollection上启用 LINQ,必须实现 IEnumerable<LaneConfigElement>

并添加以下 GetEnumerator的实现

public new IEnumerator<LaneConfigElement> GetEnumerator()
{
int count = base.Count;
for (int i = 0; i < count; i++)
{
yield return base.BaseGet(i) as LaneConfigElement;
}
}

对于那些仍然对收益率如何真正起作用感到困惑的人来说,读读 这篇好文章

从上面的文章中摘录的两个要点是

它并没有真正结束方法的执行 方法执行,并在下次调用它时(为下次调用 枚举值) ,则该方法将继续从最后一个 我认为这听起来有点令人困惑... ... (Shay Friedman)

产量不是.Net 运行时的一个特性,它只是一种 C # 语言 这个特性可以被 C # 编译器编译成简单的 IL 代码。(Lars Corneliussen)

对于那些不愿意手动编写所有配置样板的人来说,这是一个更简单的选择..。

1)从 NuGet 安装 Nerdle 自动配置

2)定义 ServiceConfig 类型(具体的类或者仅仅是一个接口都可以)

public interface IServiceConfiguration
{
int Port { get; }
ReportType ReportType { get; }
}

3)你需要一个类型来保存集合,例如。

public interface IServiceCollectionConfiguration
{
IEnumerable<IServiceConfiguration> Services { get; }
}

4)像这样添加配置部分(注意 camelCase 命名)

<configSections>
<section name="serviceCollection" type="Nerdle.AutoConfig.Section, Nerdle.AutoConfig"/>
</configSections>


<serviceCollection>
<services>
<service port="6996" reportType="File" />
<service port="7001" reportType="Other" />
</services>
</serviceCollection>

5)使用 AutoConfig 绘制地图

var services = AutoConfig.Map<IServiceCollectionConfiguration>();