65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET 2.0 自定义配置节

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.53/5 (22投票s)

2006年2月19日

8分钟阅读

viewsIcon

131644

downloadIcon

602

在 .NET 2.0 中创建自定义配置节。

引言

本文介绍如何在 .NET 2.0 中开发自定义配置节。为便于理解,本示例从 .NET 1.1 中的现有节开始,展示了将其转换为 .NET 2.0 所需的更改。当然,文章也解释了为什么应该这样做。

您可以下载 1.1 和 2.0 两个版本演示项目。2.0 版本添加了一些新功能。

配置

无论我们目标是什么框架版本或环境,第一步都是定义所需的配置属性。在本示例中,我们将使用以下简单的配置类:

Public Class ConfigurationSample
    Public OneProperty As String
    Public OneNumericValue As Integer
    Public OneElement As ConfigurationSampleElementClass
End Class

Public Class ConfigurationSampleElementClass
    Public AnotherProperty As String
End Class

注意:此处为了简化使用了字段。您已经知道,通常更倾向于使用属性而不是字段,对吧?现在,让我们看看如何从 CONFIG 文件中获取此配置信息。

背景:1.1 版本

在 .NET 1.1 中,每个配置节都由一个配置处理器管理,该处理器必须实现 IConfigurationSectionHandler 接口。在该接口的 Create 方法中,您应该查找作为参数传递的 XML 节点,并使用它来创建我们 ConfigurationSample 类的实例。

当然,对于如此简单的问题,这似乎有点复杂。已知信息来源是 XML 节点,而您想要的只是一个表示该节点的类的实例,XML 序列化就是解决我们问题的方案。

从 XML 序列化的角度来看,我们需要的配置文件可能如下所示:

<sampleConfiguration oneProperty="the value for the property"
                                            oneNumericValue="25">
    <oneElement anotherProperty="some usefull string here"/>
</sampleConfiguration>

为了使我们的 ConfigurationSample 类能够序列化为上述 XML 数据,我们需要为它添加一些属性:

Public Class ConfigurationSample
    <XmlAttributeAttribute("oneProperty")> _
                       Public OneProperty As String
    <XmlAttributeAttribute("oneNumericValue")> _
                       Public OneNumericValue As Integer
    <XmlElement("oneElement")> _
         Public OneElement As ConfigurationSampleElementClass
End Class

Public Class ConfigurationSampleElementClass
    <XmlAttributeAttribute("anotherProperty")> _
                             Public AnotherProperty As String
End Class

现在,我们只需要编写一个配置处理器,将 CONFIG 文件中的 XML 反序列化到我们的类中。这应该如下所示:

Public Class ConfigurationSampleHandler
   Implements IConfigurationSectionHandler

   Public Function Create(ByVal parent As Object, _
     ByVal configContext As Object, _
     ByVal section As System.Xml.XmlNode) As Object _
     Implements System.Configuration.IConfigurationSectionHandler.Create
        Dim ser As New XmlSerializer(GetType(ConfigurationSample), _
                   New Xml.Serialization.XmlRootAttribute(section.Name))
        Dim res As Object = ser.Deserialize(New XmlNodeReader(section))
        Return res
   End Function
End Class

并让框架知道这是我们新配置节的处理程序。这通过 CONFIG 文件中的 <configSections> 标签来实现。所以我们的 CONFIG 文件应该如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="sampleConfiguration" 
          type="Configuration_dotNet1_1.ConfigurationSampleHandler, ...
          ... Configuration_dotNet1_1" />
    </configSections>
    <sampleConfiguration 
        oneProperty="the value for the property"
        oneNumericValue="25">
        <oneElement anotherProperty="some usefull string here"/>
    </sampleConfiguration>
</configuration>

我们离完成又近了一步。我们只需要一种方法来访问此信息。为了实现这一点,我们将向 ConfigurationSample 添加一些额外的代码。

Private Shared m_current As ConfigurationSample
Public Shared ReadOnly Property Current() As ConfigurationSample
    Get
        If m_current Is Nothing Then
            m_current = DirectCast(ConfigurationSettings.GetConfig( _
                          "sampleConfiguration"), ConfigurationSample)
        End If
        Return m_current
    End Get
End Property

这样就完成了。我们的应用程序配置现在已准备就绪。所以,要读取我们配置文件中的一个值,我们只需要:

ConfigurationSample.Current.OneProperty

2.0 版本

.NET 1.1 中的实现很简单。那么,为什么在新框架版本中不能做同样的事情呢?实际上,您可以做到。但是,尽管此代码可以正确编译,您仍会收到 IDE 的一个小警告。

哎呀!那么,我们应该使用 ConfigurationManager 而不是 ConfigurationSettings 吗?这没什么大不了的。我们更改它,一切都会正常运行。但是等等,在阅读 ConfigurationManager 文档时,您会发现有很多与配置相关的新类。有一个全新的框架程序集专门用于配置。

我们首先会注意到新的 ConfigurationSectionConfigurationElement 等类。第一个是我们自定义节的基础。第二个是我们节中每个复杂属性的基础。

但是,如果代码运行良好,为什么还要更改它呢?您说得对,仅仅因为新的“时尚”就更改某些东西不是一个好的做法。所以,在继续之前,让我们找出我们 1.1 实现的一些重要问题。

  • 我们的 ConfigurationSample 属性被定义为读/写。原因很简单:XML 序列化仅适用于公共读/写字段/属性。这将误导使用我们类的任何人,因为更改仅在我们应用程序运行时才会被持久化。当然,您可以直接将配置文件打开为 XMLDocument,更改正确的 XMLNode 并保存更改。但这并非易事。
  • 如果我们(例如)需要存储任何敏感信息(数据库连接字符串),那么任何能够物理访问配置文件的人都可以读取它。

这些问题以及本文范围之外的其他问题,都由这些新的配置类来解决。

所以,让我们开始修改。正如我们已经说过的,我们必须在我们的配置类中继承自 ConfigurationSection。还有一些新属性可以让我们更精确地定义我们的配置属性。

Public Class ConfigurationSample
    Inherits System.Configuration.ConfigurationSection
    <ConfigurationProperty("oneProperty", _
                      DefaultValue:="a default Value", _
                      IsRequired:=True, IsKey:=True)> _
        Public ReadOnly Property OneProperty() As String
           Get
               Return CStr(Me("oneProperty"))
           End Get
        End Property

    <ConfigurationProperty("oneNumericValue", IsRequired:=True)> _
        Public ReadOnly Property OneNumericValue() As Integer
            Get
                Return CInt(Me("oneNumericValue"))
            End Get
        End Property

    <ConfigurationProperty("oneElement", IsRequired:=True)> _
        Public ReadOnly Property OneElement() As _
                              ConfigurationSampleElementClass
            Get
                Return DirectCast(Me("oneElement"), _
                          ConfigurationSampleElementClass)
            End Get
        End Property

    Private Shared m_current As ConfigurationSample

    Public Shared ReadOnly Property Current() As ConfigurationSample
        Get
            Return DirectCast(ConfigurationManager.GetSection( _
                "sampleConfiguration"), _
                           ConfigurationSample)
        End Get
    End Property
End Class

Public Class ConfigurationSampleElementClass
   Inherits ConfigurationElement

   <ConfigurationProperty("anotherProperty", IsRequired:=True)> _
       Public ReadOnly Property AnotherProperty() As String
           Get
               Return CStr(Me("anotherProperty"))
           End Get
       End Property
End Class

我们在这里所做的与 1.1 代码在功能上完全一致。

这里更有趣的新特性是 ConfigurationProperty 属性。有了它们,我们可以为属性定义默认值,决定属性是否必须在 CONFIG 文件中指定,甚至可以为每个属性定义验证规则。

接下来要注意的是,ConfigurationSample 现在继承自 ConfigurationSection,而 ConfigurationSampleElementClass 继承自 ConfigurationElement。您会注意到我们的配置类中由于此继承而产生了许多新属性……稍后将对此进行更多介绍。

这带来了一个副作用,即我们不再需要 ConfigurationSampleHandler 类。事实上,如果您检查 ConfigurationSection 类,您会发现该类实现了 IConfigurationSectionHandler 接口。因此,我们必须更改配置文件以反映此更改。

<configSections>
    <section name="sampleConfiguration" 
     type="Configuration_dotNet2_0.ConfigurationSample, ...
        ... Configuration_dotNet2_0" />
</configSections>

现在,我们已经实现了一个完整的 2.0 配置。让我们开始介绍一些新功能。

我们要做的第一件事是为我们的应用程序配置添加功能。查看 MSDN 文档,我们在 Configuration 类中找到了一个 Save 方法。这看起来是个不错的起点……此 Configuration 实例通过 ConfigurationManager.OpenExeConfiguration 函数获得。从这个实例中,我们可以获得我们 ConfigurationSample 节的可读/写版本。所以现在我们将开始更改我们当前的属性。

Private m_cfg As Configuration
Private Shared m_current As ConfigurationSample
Public Shared ReadOnly Property Current() As ConfigurationSample
   Get
      If m_current Is Nothing Then
         Dim cfg As Configuration
         Dim ctx As System.Web.HttpContext 
         ctx = System.Web.HttpContext.Current
         If ctx Is Nothing Then
            cfg = ConfigurationManager.OpenExeConfiguration( _
                   ConfigurationUserLevel.None)
         Else
            cfg = WebConfigurationManager.OpenWebConfiguration( _
                ctx.Request.ApplicationPath)
         End If
         m_current = DirectCast(cfg.Sections(CS_SECTION), _
                                         ConfigurationSample)
         m_current.m_cfg = cfg
      End If
      Return m_current
   End Get
End Property

在 2.0 中,WinForms 或控制台应用程序的配置加载过程与 ASP.NET 应用程序不同,因此此代码必须知道运行环境(通过检查 HttpContext.Current)以根据环境加载配置。在 ASP.NET 环境中,您必须使用 WebConfigurationManger 而不是 ConfigurationManager

接下来要注意的是,我们将 Configuration 实例的副本存储在 ConfigurationSample 实例中。稍后我们将需要它来保存更改。当然,我们必须更改 Property 定义以允许写入。类似这样:

...
<ConfigurationProperty("oneNumericValue", IsRequired:=True)> _
    Public Property OneNumericValue() As Integer
        Get
            Return CInt(Me("oneNumericValue"))
        End Get
        Set(ByVal value As Integer)
            Me("oneNumericValue") = value
        End Set
    End Property
...

注意:如果我们尝试通过 ConfigurationManager.GetSection 方法获得的 ConfigurationSection 调用 Set 方法,我们将收到一个 ConfigurationErrorsException,并带有“配置是只读的”消息。

现在我们可以保存配置值。为此,我们将向 ConfigurationSample 类添加一个新方法。

Public Sub Save()
    Me.SectionInformation.ForceSave = True
    m_cfg.Save(ConfigurationSaveMode.Full)
End Sub

正如您所见,我们只需要调用 Configuration.Save 方法。ForceSave=True 行指示框架将所有属性保存到 CONFIG 文件,无论它们的值是否已更改。如果某些属性未在 CONFIG 文件中设置,它将采用其默认值(在 ConfigurationProperty 属性中指定 DefaultValue),这可能会很有用。在这种情况下,此默认值将被保存到 CONFIG 文件。

请注意,在 ASP.NET 应用程序中,由于 web.config 文件被修改,这会导致应用程序重新启动,因此您必须考虑到这一点,并且只有在真正必要时才修改配置。

现在,为了完成本文,我们将向此类添加加密自身的功能。要实现这一点,我们只需要向 ConfigurationSample 类添加另一个方法。

Public Sub Encript(ByVal encrypt As Boolean)
    If encript Then
        Me.SectionInformation.ProtectSection( _
        "DataProtectionConfigurationProvider")
    Else
        Me.SectionInformation.UnprotectSection()
    End If
    Me.Save()
End Sub

通过此方法,我们使用指定的提供程序(“DataProtectionConfigurationProvider”)来保护(或取消保护,取决于参数)配置节。此提供程序由框架默认提供,它基于 Windows CAPI。我们应该能够创建自己的提供程序,但这超出了本文的范围。一旦在我们的 <sampleConfiguration> 节中执行此方法,它将如下所示:

<sampleConfiguration 
  configProtectionProvider="DataProtectionConfigurationProvider">
    <EncryptedData>
        <CipherData>
            <CipherValue>AQAAANCMnd8BFdERjHoAwE/C ...
        ... 4794DC2Is=</CipherValue>
        </CipherData>
    </EncryptedData>
</sampleConfiguration>

注意:此保护是特定于计算机的,因此一旦被保护,该节只能在该计算机上成功解密。

澄清:重要的是要记住,此加密并不能保护您的设置免受系统管理员的好奇。它旨在保护您免受任何试图通过某些意外的“后门”访问(通常由框架保护)的 CONFIG 文件的黑客攻击。例如,您应用程序中的一个页面,通过带有文件路径的 GET 或 POST 参数将存储的文档提供给客户端(非常危险的页面……但这超出了本文的范围)。因此,它保护您免受任何可以**读取**您的 CONFIG 文件的人的侵害,但不能保护您免受任何可以在机器上**执行**某些操作的人的侵害。请记住,**没有任何东西**可以保护您免受拥有您计算机或服务器上管理员权限的人的侵害。

结论

这个新版本的 .NET Framework 包含许多与 Configuration 相关的新功能。本文是一个小示例,让您开始尝试,但还有许多其他很棒的功能可以供您探索,例如:

  • 从单独的配置文件读取节。有关更多信息,请查看 SectionInformation.Source 属性。
  • 为某些属性定义类型验证器。查看 ConfigurationValidator 属性及其派生类。

现在是时候下载示例并开始阅读文档了。这里有一些您需要的重要链接:

更新

  • 2006/02/23:根据读者疑问进行了一些修改
    • ConfigurationSample.Current 函数中添加了双环境(Web/Windows)配置加载过程。
    • 添加了加密说明。
    • 更新了 2.0 示例以显示双环境功能。现在是一个三项目解决方案,包含一个 WinForms 应用程序、一个 ASP.NET 应用程序和一个包含 ConfigurationSample 类的通用 DLL。ASP.NET 应用程序向您展示了修改 web.config 文件后应用程序如何重新启动。
© . All rights reserved.