如何使用ConfigurationElementCollection实现ConfigurationSection

我正试图在一个项目中实现一个自定义的configuration部分,并继续运行我不明白的exception。 我希望有人能填补这里的空白。

我有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); } } 

我缺less的部分是为处理程序做什么。 本来,我试图实现一个IConfigurationSectionHandler但发现了两件事情:

  1. 它没有工作
  2. 它已被弃用。

我现在完全失去了什么,所以我可以从configuration中读取我的数据。 请任何帮助!

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

你的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类保持不变。

你需要一个新的class级:

 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]; 

如果您正在寻找像下面这样的自定义configuration部分

 <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> 

那么你可以使用我的configuration部分的实现,以便开始添加System.Configuration程序集引用到你的项目

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

凭证元素

 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; } } } 

PrimaryAgent和SecondaryAgent

两者都具有相同的属性,并且看起来像是一个主服务器和一个故障转移服务器的地址,所以您只需要为以下两者创build一个元素类

 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,因为没有区别。 您只需创build一个与上面相同的类,只有一个属性。 让我们看看如何实现Lanes集合

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

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一个属性是Enumeration,如果您尝试在Enumeration应用程序中未定义的configuration中使用其他任何值,将在启动时引发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"你可以select任何你喜欢的collections入口项目,我更喜欢使用“添加”默认的一个,但我改变它只是为了这个职位。

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

CustomApplicationConfigSection

 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)中使用这个新发明的configuration部分之前,你只需要告诉你应用程序已经发明了自己的configuration部分并给予一定的尊重,为此你必须添加以下行在app.config(可能是在根标签开始之后)。

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

注意: MyAssemblyName应该没有.dll例如,如果您的程序集文件名是myDll.dll然后使用myDll而不是myDll.dll

检索此configuration使用下面的代码行中的任何地方在您的应用程序

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

我希望上面的post能够帮助你开始一些复杂的自定义configuration部分。

快乐编码:)

****编辑****要在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; } } 

对于那些仍然对产量如何工作感到困惑的人来说,阅读这篇不错的文章

从上面的文章中取得的两个要点是

它并不真正结束该方法的执行。 yield return会暂停方法执行,下一次调用它时(对于下一个枚举值),方法将继续从最后的yield return调用中执行。 这听起来有点混乱,我认为… (Shay Friedman)

良率不是.Net运行时的function。 它只是一个C#语言特性,它被C#编译器编译成简单的IL代码。 (Lars Corneliussen)

这是configuration集合的通用代码:

 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 ,你可以在configuration部分简单地使用它(这是我的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"]; } } } 

configuration元素是configuration在这里:

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

configuration文件将如下所示:

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

希望它有帮助!

对于那些不想手动编写所有configuration样板文件的人来说,这是一个更简单的select。

1)从NuGet安装Nerdle.AutoConfig

2)定义你的ServiceConfigtypes(既可以是具体的类也可以是一个接口)

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

3)你将需要一个types来举行收集,例如

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

4)像这样添加configuration部分(注意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>(); 

尝试从ConfigurationSectioninheritance。 菲尔·哈克的这篇博客文章就是一个例子。

确认,根据IConfigurationSectionHandler的文档:

在.NET Framework 2.0及更高版本中,您必须从ConfigurationSection类派生以实现相关的configuration节处理程序。