关于c#:如何使用ConfigurationElementCollection实现ConfigurationSection

How to implement a ConfigurationSection with a ConfigurationElementCollection

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

我有一个App.config看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
<?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>

我有一个这样定义的EDOCX1[1]元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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定义如下:

2

我缺少的部分是处理程序要做什么。最初,我试图实现一个IConfigurationSectionHandler,但发现了两件事:

  • 它不起作用
  • 它被否决了。
  • 我现在完全不知道该怎么做了,所以我可以从config中读取数据。请帮忙!


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

    app.config应该如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
       <configSections>
          <section name="ServicesSection" type="RT.Core.Config.ServiceConfigurationSection, RT.Core"/>
       </configSections>
       <ServicesSection>
          <Services>
             
             
          </Services>
       </ServicesSection>
    </configuration>

    您的ServiceConfigServiceCollection类保持不变。

    你需要一个新的班级:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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"];
          }
       }
    }

    这样就可以了。要使用它,您可以使用:

    2


    如果您要查找如下自定义配置节:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <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程序集引用。

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

    凭证元素

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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;
                }
            }
        }

    一级剂和二级剂

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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,因为它没有区别。您只需要用一个属性创建一个与上面相同的类。让我们看看如何实现车道收集

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

    LaneConfigement公司

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    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。好的,让我们转到集合定义。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    [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

    自定义应用程序配置节

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    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中添加以下行(可能在根标记开始之后)。

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

    注意:myassemblyname不应包含.dll,例如,如果程序集文件名为mydll.dll,请使用mydll而不是mydll.dll。

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

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

    我希望上面的文章可以帮助您开始使用一些复杂的自定义配置部分。

    快乐编码:

    ***编辑***要在LaneConfigCollection上启用linq,必须实现IEnumerable

    并在执行GetEnumerator后增加

    1
    2
    3
    4
    5
    6
    7
    8
    public new IEnumerator<LaneConfigElement> GetEnumerator()
            {
                int count = base.Count;
                for (int i = 0; i < count; i++)
                {
                    yield return base.BaseGet(i) as LaneConfigElement;
                }
            }

    对于那些仍然对产量如何真正起作用感到困惑的人来说,请阅读这篇好文章。

    从以上文章中可以得出两个关键点:

    it doesn’t really end the method’s execution. yield return pauses the
    method execution and the next time you call it (for the next
    enumeration value), the method will continue to execute from the last
    yield return call. It sounds a bit confusing I think… (Shay Friedman)

    Yield is not a feature of the .Net runtime. It is just a C# language
    feature which gets compiled into simple IL code by the C# compiler. (Lars Corneliussen)


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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之后,您可以在配置部分简单地使用它(这是我的调度器的一个示例):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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元素在此处配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class EventsDispatchConfigurationElement : ConfigurationElement
    {
        [ConfigurationProperty("name", IsRequired = true)]
        public string Name
        {
            get
            {
                return (string) this["name"];
            }
            set
            {
                this["name"] = value;
            }
        }
    }

    配置文件如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    <?xml version="1.0" encoding="utf-8" ?>
      <dispatcherConfigurationSection>
        <eventsDispatches>
          </add>
          </add>
          </add>
        </eventsDispatches>
      </dispatcherConfigurationSection>

    希望有帮助!


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

    1)从nuget安装nerdle.autoconfig

    2)定义您的serviceconfig类型(具体的类或只是一个接口,两者都可以)

    1
    2
    3
    4
    5
    public interface IServiceConfiguration
    {
        int Port { get; }
        ReportType ReportType { get; }
    }

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

    1
    2
    3
    4
    public interface IServiceCollectionConfiguration
    {
        IEnumerable<IServiceConfiguration> Services { get; }
    }

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

    2

    5)自动配置地图

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


    尝试从配置节继承。菲尔·哈克的这篇博客文章就是一个例子。

    已确认,根据IConfigurationSectionHandler的文档:

    0