如何使用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
但发现了两件事情:
- 它没有工作
- 它已被弃用。
我现在完全失去了什么,所以我可以从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>
您的ServiceConfig
和ServiceCollection
类保持不变。
你需要一个新的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; } } }
现在你可以看到,我们有两个名字为PrimaryAgent
和SecondaryAgent
属性,现在你可以很容易的理解为什么我们只有一个实现类对这两个元素。
在你的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节处理程序。