.NET 2.0 自定义配置节






4.53/5 (22投票s)
2006年2月19日
8分钟阅读

131644

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 文档时,您会发现有很多与配置相关的新类。有一个全新的框架程序集专门用于配置。
我们首先会注意到新的 ConfigurationSection
、ConfigurationElement
等类。第一个是我们自定义节的基础。第二个是我们节中每个复杂属性的基础。
但是,如果代码运行良好,为什么还要更改它呢?您说得对,仅仅因为新的“时尚”就更改某些东西不是一个好的做法。所以,在继续之前,让我们找出我们 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 文件后应用程序如何重新启动。
- 在