Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ASP.NET configuration management: an alternative to web.config

0.00/5 (No votes)
14 Feb 2005 1  
An alternative and flexible way to manage config settings for multiple web applications.

Introduction

ASP.NET web applications that follow the classic configuration settings management technique used nowadays store configuration values in the web.config file. This is an XML configuration file, containing various settings: some of them are targeted to the ASP.NET engine itself (for example: globalization settings, authentication settings, compilation settings); other settings are targeted to the application, and are known as "AppSettings" (from the name of the XML node containing them inside the web.config file). The adoption of a configuration settings management strategy based on web.config is suitable for single web applications, but it quickly becomes unmanageable if you need to maintain the configuration settings for a family of similar web applications, especially when you need to keep aligned some common settings on more and more web.config files. Of course, if the same machine hosts multiple web sites with similar application settings, you always have the chance to store the common settings in the machine.config file, that acts as a "master" configuration file for all the web applications hosted on that machine. But also this approach quickly becomes unusable if your deployment scenario is built on a web farm, with multiple web servers.

I worked for a single project that had lots of web applications, targeted to multiple languages, hosted on multiple, load-balanced web servers, on multiple environments. The number of web applications (and of corresponding web.config files) to be managed was about 105, so I decided to think of a different way to manage those thousands of settings.

This article describes the configuration management solution (alternative to the classic web.config file usage) I adopted. It addresses only the storage of the AppSettings: all the other configurations, targeted to the web engine (like: globalization, compilation, authentication), are still stored in web.config.

Configuration settings storage

I decided to store all the settings on a Microsoft SQL Server 2000 database table, named WebSettings, in a database of your choice, that from now on I'll call CMS (Configuration Management System). The WebSettings table structure contains three main fields:

  • WebSite, a string indicating the web site that is reading a particular configuration setting (a.k.a.: web site identifier).
  • KeyName, a string containing the name of the configuration setting (something similar to the value of the original key attribute in an add node of the web.config's appSettings).
  • KeyValue, a string containing the value of the configuration setting (something similar to the value of the original value attribute in an add node of the web.config's appSettings)

An optional KeyDescr field can contain a description of the configuration key, specifying the allowed values or some setting guidelines. A primary key on the WebSite and KeyName fields will guarantee the uniqueness of each setting.

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

Each KeyName/KeyValue pair in the WebSettings table must be associated with a particular WebSite. When a particular web site needs a configuration value, a call is given to the function GetWebSetting() - see the code below - and the correct KeyValue is retrieved based on the KeyName supplied and on the requesting web application identity. The GetWebSetting() function definitely implements a functionality similar to the .NET Framework collection ConfigurationSettings.AppSettings() in the System.Configuration namespace. The difference between the two is that GetWebsetting() always returns a String value (eventually an empty string if the desired configuration setting value is not found in the WebSettings table), and never the Nothing value.

The GetWebSetting() function determines the requesting web application identity by inspecting the value of the WebSiteIdentifier key in the web.config associated to the current HTTP context. The WebSiteIdentifier is one of the only two AppSettings keys needed in the web.config file of the calling web application: it permits the identification of the web site itself when querying the WebSettings table. The second AppSettings key is named WebSettingsConnString and contains the connection string needed to connect to the CMS database hosting the WebSettings table:

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

Hierarchical configuration

The WebSiteIdentifier key is a dot-separated string, containing more substrings identifying classes (or sets) of similar applications. In my specific context, the WebSiteIdentifier was composed of three parts: WebSiteType.Area.Language. When a configuration KeyName/KeyValue pair has to be set for a specific web site, the corresponding WebSite field in the WebSettings table will be filled with the exact three-part web site identifier of the related web application. For configuration KeyName/KeyValue pairs that are common to multiple, similar web applications, you can enter a single entry in the WebSettings table, indicating that it is applicable to a set of web applications; to do so, you will use the special word _DefaultSettings (be sure to start this word with an "underscore" character) as a part of the three-part-dotted identifier. For example:

A WebSettings.WebSite field filled with... will indicate a KeyName/KeyValue pair...
MySite.Europe.English only applicable to the single, specific English European "MySite" web site
MySite.Europe._DefaultSettings applicable to all European "MySite" web sites (any language)
MySite._DefaultSettings applicable to all "MySite" web sites (any language, any area)
_DefaultSettings applicable to all web sites ("MySite" and others)

The presence of a _DefaultSettings setting doesn't exclude the possibility to have some specializations for a particular site. In fact, the GetWebSetting() function always looks for a KeyName/KeyValue pair starting from the most specific WebSiteIdentifier it finds in the WebSettings.WebSite field. So, for example, the search matching order for my specific hierarchy was as follows:

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

Caching

When the GetWebSetting function is called for the first time, it reads from the CMS database (pointed by the WebSettingsConnString AppSettings key) the settings related to the requesting application. These values are then cached in the ASP.NET Cache object, in order to minimize the database access during subsequent calls to the GetWebSetting() function. These values remain in the web server cache until one of the following events occurs:

  • The Application Domain related to the web application is restarted (this happens, for example, if: the web.config file of the application is modified; the bin directory of the application is modified; the Application Pool hosting the application is recycled)
  • The ASP.NET engine flushes the cache to gain some memory for other tasks

Every time the GetWebSetting() function is called, it checks for configuration values in the cache, if they are present, they are used as they are; otherwise, they are re-read from the database.

If the GetWebSetting() function is called specifying True as a second (optional) parameter, the configuration value is anyway read from the database (and also all the other settings for that web application are reloaded).

If you change some values in the WebSettings table, in order to make them take effect, you have to force a cache flushing so that the GetWebSetting() function re-loads values from the database. You don't need to restart the Application Domain or recycle the Application Pool: to force the configuration settings reload, you can just do a call like the following:

GetWebSetting("", True)

You could implement a special administrative ASPX web page, that - when called - flushes the web settings cache simply invoking the GetWebSetting() function with the second parameter set to True. Be sure to call it on all the web servers involved in the change (don't forget that the ASP.NET Cache object lives in the memory of each server in the farm).

The code

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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here