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

ASP.NET 配置管理:web.config 的替代方案

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.62/5 (12投票s)

2005年2月15日

CPOL

6分钟阅读

viewsIcon

81493

一种替代且灵活的方式来管理多个 Web 应用程序的配置设置。

引言

如今,遵循经典配置设置管理技术的 ASP.NET Web 应用程序将配置值存储在 web.config 文件中。这是一个 XML 配置文件,包含各种设置:其中一些针对 ASP.NET 引擎本身(例如:全局化设置、身份验证设置、编译设置);其他设置针对应用程序,被称为“AppSettings”(来自 web.config 文件中包含它们的 XML 节点名称)。采用基于 web.config 的配置设置管理策略适用于单个 Web 应用程序,但是如果您需要维护一组类似 Web 应用程序的配置设置,尤其是在您需要保持多个 web.config 文件中的一些通用设置一致时,它很快就会变得难以管理。当然,如果同一台机器托管了具有类似应用程序设置的多个网站,您始终有机会将通用设置存储在 machine.config 文件中,该文件充当该机器上所有托管 Web 应用程序的“主”配置文件。但是,如果您的部署场景建立在具有多个 Web 服务器的 Web 服务器场上,这种方法也很快变得不可用。

我曾在一个项目中工作,该项目有大量的 Web 应用程序,针对多种语言,托管在多个负载均衡的 Web 服务器上,以及多个环境中。需要管理的 Web 应用程序(和相应的 web.config 文件)数量约为 105 个,所以我决定考虑一种不同的方式来管理这些数千个设置。

本文描述了我采用的配置管理解决方案(替代经典的 web.config 文件用法)。它只涉及 AppSettings 的存储:所有其他针对 Web 引擎的配置(如:全局化、编译、身份验证)仍然存储在 web.config 中。

配置设置存储

我决定将所有设置存储在一个 Microsoft SQL Server 2000 数据库表中,名为 WebSettings,位于您选择的数据库中,从现在起我将其称为 CMS(配置管理系统)。WebSettings 表结构包含三个主要字段

  • WebSite,一个字符串,指示正在读取特定配置设置的网站(即:网站标识符)。
  • KeyName,一个字符串,包含配置设置的名称(类似于 web.configappSettingsadd 节点中原始键属性的值)。
  • KeyValue,一个字符串,包含配置设置的值(类似于 web.configappSettingsadd 节点中原始 value 属性的值)

一个可选的 KeyDescr 字段可以包含配置键的描述,指定允许的值或一些设置指南。WebSiteKeyName 字段上的主键将保证每个设置的唯一性。

CREATE TABLE WebSettings (
    WebSite varchar(64) NOT NULL ,
    KeyName varchar(64) NOT NULL ,
    KeyValue varchar(1024) NOT NULL ,
    KeyDescr text NULL )
GO

ALTER TABLE WebSettings WITH NOCHECK ADD 
    CONSTRAINT PK_WebSettings PRIMARY KEY  NONCLUSTERED 
    ( WebSite, KeyName )
GO

WebSettings 表中的每个 KeyName/KeyValue 对都必须与特定的 WebSite 相关联。当特定网站需要配置值时,会调用函数 GetWebSetting()(参见下面的代码),并根据提供的 KeyName 和请求的 Web 应用程序身份检索正确的 KeyValueGetWebSetting() 函数明确实现了 .NET Framework System.Configuration 命名空间中 ConfigurationSettings.AppSettings() 集合类似的功能。两者之间的区别在于 GetWebsetting() 总是返回一个 String 值(如果 WebSettings 表中找不到所需的配置设置值,则可能返回空字符串),而不是 Nothing 值。

GetWebSetting() 函数通过检查与当前 HTTP 上下文关联的 web.config 文件中 WebSiteIdentifier 键的值来确定请求的 Web 应用程序身份。WebSiteIdentifier 是调用 Web 应用程序的 web.config 文件中仅有的两个必需的 AppSettings 键之一:它允许在查询 WebSettings 表时识别网站本身。第二个 AppSettings 键名为 WebSettingsConnString,包含连接到托管 WebSettings 表的 CMS 数据库所需的连接字符串

<appSettings>
  <add key=
   "WebSiteIdentifier" value="MySite.Europe.English" />
  <add key=
   "WebSettingsConnString" value="server=...;database=cms;uid=...;pwd=..." />
</appSettings>

分层配置

WebSiteIdentifier 键是一个点分隔的字符串,包含多个子字符串,用于识别类似应用程序的类别(或集合)。在我特定的上下文中,WebSiteIdentifier 由三部分组成:WebSiteType.Area.Language。当需要为特定网站设置配置 KeyName/KeyValue 对时,WebSettings 表中对应的 WebSite 字段将填充相关 Web 应用程序的精确三部分网站标识符。对于多个类似 Web 应用程序通用的配置 KeyName/KeyValue 对,您可以在 WebSettings 表中输入一个单独的条目,表明它适用于一组 Web 应用程序;为此,您将使用特殊单词 _DefaultSettings(确保这个单词以“下划线”字符开头)作为三部分点分隔标识符的一部分。例如

WebSettings.WebSite 字段填充为... 将指示一个 KeyName/KeyValue 对...
MySite.Europe.English 仅适用于单个特定的英语欧洲“MySite”网站
MySite.Europe._DefaultSettings 适用于所有欧洲“MySite”网站(任何语言)
MySite._DefaultSettings 适用于所有“MySite”网站(任何语言,任何区域)
_DefaultSettings 适用于所有网站(“MySite”及其他)

_DefaultSettings 设置的存在并不排除为特定站点进行一些专门化的可能性。事实上,GetWebSetting() 函数总是从它在 WebSettings.WebSite 字段中找到的最具体的 WebSiteIdentifier 开始查找 KeyName/KeyValue 对。因此,例如,我的特定层次结构的搜索匹配顺序如下

  • WebSiteType.Area.Language
  • WebSiteType.Area._DefaultSettings
  • WebSiteType._DefaultSettings
  • _DefaultSettings

缓存

GetWebSetting 函数首次调用时,它从 CMS 数据库(由 WebSettingsConnString AppSettings 键指向)读取与请求应用程序相关的设置。然后将这些值缓存到 ASP.NET Cache 对象中,以最大程度地减少后续调用 GetWebSetting() 函数时的数据库访问。这些值保留在 Web 服务器缓存中,直到发生以下事件之一

  • 与 Web 应用程序相关的应用程序域重新启动(例如,如果:应用程序的 web.config 文件被修改;应用程序的 bin 目录被修改;托管应用程序的应用程序池被回收)
  • ASP.NET 引擎刷新缓存以获取其他任务的内存

每次调用 GetWebSetting() 函数时,它都会检查缓存中的配置值,如果存在,则直接使用;否则,它们将从数据库中重新读取。

如果调用 GetWebSetting() 函数时将第二个(可选)参数指定为 True,则配置值将始终从数据库中读取(并且该 Web 应用程序的所有其他设置也将重新加载)。

如果您更改了 WebSettings 表中的某些值,为了使其生效,您必须强制刷新缓存,以便 GetWebSetting() 函数从数据库重新加载值。您无需重新启动应用程序域或回收应用程序池:要强制重新加载配置设置,您只需进行如下调用

GetWebSetting("", True)

您可以实现一个特殊的管理 ASPX 网页,该网页在被调用时,通过将第二个参数设置为 True 来简单调用 GetWebSetting() 函数,从而刷新 Web 设置缓存。请务必在涉及更改的所有 Web 服务器上调用它(不要忘记 ASP.NET Cache 对象存在于服务器场中每个服务器的内存中)。

代码

Imports System.Configuration

Public Function GetWebSetting(ByVal Key As String, _
    Optional ByVal ForceCacheRefresh As Boolean = False) As String
    Dim dtWebSettings As DataTable
    If ForceCacheRefresh Then
      HttpContext.Current.Cache.Remove("WebSettingsCache")
    End If
    If HttpContext.Current.Cache("WebSettingsCache") Is Nothing Then
      dtWebSettings = 
              New DataTable
      Dim connStr As String = 
              ConfigurationSettings.AppSettings("WebSettingsConnString") & ""
      If connStr = "" Then
        Throw New Exception("WebSettingsConnString key missing in web.config")
        Exit Function
      End If
      Dim siteIdentifier As String = 
              ConfigurationSettings.AppSettings("WebSiteIdentifier") & ""
      If siteIdentifier = "" Then
        Throw New Exception("WebSiteIdentifier key missing in web.config")
        Exit Function
      End If
      Dim cnn As New SqlConnection(connStr)
      Dim cmd As SqlCommand = cnn.CreateCommand()
      ' Select only KeyName and KeyValue 
      ' (KeyDescr is not retrieved - it's a text field)
      ' The ORDER BY clause guarantees that "_DefaultSettings" 
      ' settings are the last ones
      Dim SQLstr As String
      Dim WebSitePart As String = siteIdentifier
      SQLstr = "SELECT KeyName, KeyValue FROM WebSettings" &_
                            " WHERE WebSite='" & siteIdentifier & "'"
      Do While WebSitePart.IndexOf(".") >= 0
        WebSitePart = WebSitePart.Substring(0, WebSitePart.LastIndexOf("."))
        SQLstr & = " OR WebSite='" & WebSitePart & "._DefaultSettings'"
      Loop
      SQLstr & = " OR WebSite='_DefaultSettings' ORDER BY WebSite DESC"
      cmd.CommandText = SQLstr
      Dim da As New SqlDataAdapter(cmd)
      da.Fill(dtWebSettings)
      HttpContext.Current.Cache.Add("WebSettingsCache", _
                   dtWebSettings, Nothing, Date.MaxValue, TimeSpan.Zero, _
                   Caching.CacheItemPriority.High, Nothing)
    Else
      dtWebSettings = 
         CType(HttpContext.Current.Cache("WebSettingsCache"), DataTable)
    End If

    If Key <> "" Then
      Dim drs As DataRow() = 
             dtWebSettings.Select("KeyName='" & Key & "'")
      If drs.Length = 0 Then
        ' The KeyName was not found
        Return ""
      Else
        ' The KeyName was found: returned rows can be more 
        ' than one (if DefaultSettings are there)
        ' Return the value of the first record(the one with 
        ' the most specific web site definition)
        Return drs(0)("KeyValue").ToString()
      End If
    Else
      ' Key can be an empty string, for example when you want 
      ' only the refresh of the Cache
      ' and you call:  GetWebSetting("", True)
      Return ""
    End If

  End Function
© . All rights reserved.