Implementing custom configuration section handler

2019-03-01 10:30发布

问题:

gleanings from a variety of sources (including stackOverlflow), however when I come to use it, I get the following error message

"The Configuration property 'deviceconfig' may not be derived from ConfigurationSection."

I have been struggling with this for the better part of a day now, and am no nearer a solution, so any help in the matter would be greatly appreciated. I am a newbie in implementing custom config sections so please treat me gently :-)

The app.config file looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="deviceconfig"  type="CNIMonitor.Core.CustomConfig.DeviceConfig,Core"/>
  </configSections>
<system.diagnostics>
    <sources>
        <!-- This section defines the logging configuration for My.Application.Log -->
        <source name="DefaultSource" switchName="DefaultSwitch">
            <listeners>
                <add name="FileLog"/>
                <!-- Uncomment the below section to write to the Application Event Log -->
                <!--<add name="EventLog"/>-->
            </listeners>
        </source>
    </sources>
    <switches>
        <add name="DefaultSwitch" value="Information" />
    </switches>
    <sharedListeners>
        <add name="FileLog"
             type="Microsoft.VisualBasic.Logging.FileLogTraceListener, Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" 
             initializeData="FileLogWriter"/>
        <!-- Uncomment the below section and replace APPLICATION_NAME with the name of your application to write to the Application Event Log -->
        <!--<add name="EventLog" type="System.Diagnostics.EventLogTraceListener" initializeData="APPLICATION_NAME"/> -->
    </sharedListeners>
</system.diagnostics>
<deviceconfig>
  <devices>          
      <device deviceid = "1" 
              name = "localhost" 
              ipaddress="127.0.0.1" 
              port="10000"
              status="not listening"
              message="no message"
      />
      <device deviceid ="2" 
              name ="2nd localhost" 
              ipaddress="127.0.0.1" 
              port="20000" 
              status="not listening"
              message="no message"
      />
  </devices>  
</deviceconfig>

The Custom Section Handler code as follows:

Imports System.Configuration

Namespace CNIMonitor.Core.CustomConfig

Public Class DeviceConfig
    Inherits ConfigurationSection
    <ConfigurationProperty("deviceconfig")> _
    Public ReadOnly Property DeviceConfig() As DeviceConfig
        Get
            Return CType(MyBase.Item("deviceconfig"), DeviceConfig)
        End Get
    End Property
End Class

<ConfigurationCollectionAttribute(GetType(Device))> _
Public Class Devices
    Inherits ConfigurationElementCollection
    Protected Overrides Function CreateNewElement() As ConfigurationElement
        Return New Device
    End Function
    Protected Overrides Function GetElementKey _
    (ByVal element As ConfigurationElement) As Object
        Return CType(element, Device).DeviceID
    End Function
    Public Sub Add(ByVal element As Device)
        Me.BaseAdd(element)
    End Sub
End Class
Public Class Device
    Inherits ConfigurationElement
    <ConfigurationProperty("deviceid", DefaultValue:="", IsKey:=True, IsRequired:=True)> _
    Public Property DeviceID() As String
        Get
            Return CType(MyBase.Item("deviceid"), String)
        End Get
        Set(ByVal value As String)
            MyBase.Item("deviceid") = value
        End Set
    End Property
    <ConfigurationProperty("hostname", DefaultValue:="", IsKey:=True, IsRequired:=False)> _
    Public Property HostName() As String
        Get
            Return CType(MyBase.Item("RegisteredDate"), String)
        End Get
        Set(ByVal value As String)
            MyBase.Item("RegisteredDate") = value
        End Set
    End Property
    <ConfigurationProperty("ipaddress", DefaultValue:="", IsKey:=True, IsRequired:=False)> _
    Public Property IpAddress() As String
        Get
            Return CType(MyBase.Item("ipaddress"), String)
        End Get
        Set(ByVal value As String)
            MyBase.Item("ipaddress") = value
        End Set
    End Property
    <ConfigurationProperty("port", DefaultValue:="", IsKey:=True, IsRequired:=False)> _
    Public Property Port() As String
        Get
            Return CType(MyBase.Item("port"), String)
        End Get
        Set(ByVal value As String)
            MyBase.Item("port") = value
        End Set
    End Property
    <ConfigurationProperty("status", DefaultValue:="", IsKey:=False, IsRequired:=False)> _
    Public Property Status() As String
        Get
            Return CType(MyBase.Item("status"), String)
        End Get
        Set(ByVal value As String)
            MyBase.Item("status") = value
        End Set
    End Property
    <ConfigurationProperty("message", DefaultValue:="", IsKey:=False, IsRequired:=False)> _
    Public Property Message() As String
        Get
            Return CType(MyBase.Item("message"), String)
        End Get
        Set(ByVal value As String)
            MyBase.Item("message") = value
        End Set
    End Property
End Class

End Namespace

回答1:

The DeviceConfig property is not needed at the top level. What you do need to do is add a property for your ConfigurationElementCollection devices. I would also rename your Devices class something a little less ambiguous, say, DeviceElementCollection. Try this:

Public Class DeviceConfig    
    Inherits ConfigurationSection    

    <ConfigurationProperty("Devices")> _    
    Public ReadOnly Property Devices() As DeviceElementCollection    
        Get    
            Return CType(Me("Devices"), DeviceElementCollection)    
        End Get    
    End Property    
End Class    

Then, in your definition for DeviceElementCollection, you may have problems if you don't add the following:

Public Overrides ReadOnly Property CollectionType() As System.Configuration.ConfigurationElementCollectionType
    Get
        Return ConfigurationElementCollectionType.BasicMap
    End Get
End Property

Protected Overrides ReadOnly Property ElementName() As String
    Get
        Return "Device"
    End Get
End Property

I wrote up a pretty long answer to a similar question (sorry, in C#) here.

Update - how to use in code

Dim deviceConfiguration as DeviceConfig = ConfigurationManager.GetSection("deviceconfigs")
For Each device As Device in deviceConfiguration.Devices
    '...whatever you need to do
Next