- A Word on the Metabase
- Walk-Through of web.config's Hierarchical Structure
- System.Configuration and System.Web.Configuration Namespaces
- Accessing web.config Using System.Xml
- Chapter Summary
System.Configuration and System.Web.Configuration Namespaces
Because the configuration files are XML, you probably have already envisioned different ways to access the files and work with the settings in them. It just so happens that you don't have to start coding a custom configuration file reader yet because a rich API already exists for working with the configuration system files.
The .NET Framework supplies classes for working with configuration files in the System.Configuration and System.Web.Configuration namespaces. As you can infer from their names, the System.Web.Configuration namespace is designed to support the web.config file, and the System.Configuration namespace contains classes that support the configuration system as a whole. Take a look at the classes that are available in these namespaces and the methods they provide.
System.Configuration
The System.Configuration namespace contains the classes that are used to programmatically access .NET Framework configuration files. It also supplies objects for providing custom section handlers and handling errors in configuration files. The System.Configuration namespace provides the classes that are shown in Table 7.9.
Table 7.9 Classes of the System.Configuration Namespace
Class Name |
Description |
AppSettingsReader |
Provides a single method for reading a value from the appSettings section. |
ConfigurationException |
Represents the exception thrown due to an error in a configuration file section. Provides methods for serializing the exception and determining the source configuration file and the source line number of the section node in the file. |
ConfigurationSettings |
Provides access to the configuration settings in a specified section. |
DictionarySectionHandler |
Reads key-value pair information for a section. |
IgnoreSectionHandler |
Provides a section handler for sections read and handled by systems other than System.Configuration. |
NameValueSectionHandler |
Defines a section handler for providing name-value section information. |
SingleTagSectionHandler |
Defines a section handler for providing access to attributes in a section. |
In addition to the classes defined, the System.Configuration namespace also includes a single interface definition, IConfigurationSectionHandler. This interface is required for defined section handlers. The following classes support this interface:
-
DictionarySectionHandler
-
IgnoreSectionHandler
-
NameValueSectionHandler
-
SingleTagSectionHandler
Reading Custom Settings
The configuration file API provides a set of APIs for retrieving settings that are defined in the appSettings section, as well as for retrieving settings from custom sections. Two main methods of retrieving custom values from the appSettings section exist: using the AppSettingsReader class and using the ConfigurationSettings class.
The AppSettingsReader class supports one basic method, GetValue, which gets a single value based on its key from the configuration system.
The ConfigurationSettings class exposes two methods:
AppSettingsRetrieves a custom setting from the appSettings section.
GetConfigRetrieves a custom setting from a custom section.
The code for retrieving application settings using the ConfigurationSettings.AppSettings class is similar to the code that uses the AppSettingsReader class. However, there's one main difference: If the key is not found in the configuration file using the ConfigurationSettings.AppSettings class, no error is thrown. This contrasts the AppSettingsReader, which throws an error if the specified key doesn't exist.
Listing 7.5 demonstrates how each method can easily be used by using the sample web.config file that was defined in Listing 7.1.
Listing 7.5 Reading Values from appSettings
<%@ Import Namespace="System.Configuration"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <title>Custom appSettings</title> <script language="vb" runat="server" > Private Sub Button_Click(ByVal sender As System.Object, ByVal
e As System.EventArgs) Try If Me.optConfigurationSettings.Checked Then lblResults.Text = "The startup table value is:
" & ConfigurationSettings.AppSettings
(txtKey.Text) Else Dim reader As AppSettingsReader = New
AppSettingsReader() lblResults.Text = "The value is: " &
reader.GetValue(txtKey.Text, GetType(String)) End If Catch generalExcep As System.Exception lblResults.Text = generalExcep.ToString() End Try End Sub </script> </HEAD> <body> <form runat="server"> <asp:textbox
id="txtKey" style="LEFT: 150px; POSITION: absolute;
TOP: 14px" runat="server" /> <asp:label
id="lblKey" style="LEFT: 10px; POSITION: absolute;
TOP: 15px" runat="server">
Enter the value key:</asp:label> <asp:radiobutton
id="optConfigurationSettings"
style="LEFT: 41px; POSITION: absolute; TOP: 63px"
runat="server" Width="186px"
Text="ConfigurationSettings.AppSettings"
Checked="True"
GroupName="method"></asp:radiobutton> <asp:radiobutton
id="optAppSettingsReader"
style="LEFT: 41px; POSITION: absolute; TOP: 89px"
runat="server" Width="186px"
Text="AppSettingsReader.GetValue"
GroupName="method"></asp:radiobutton> <asp:button
id="Button1"
style="LEFT: 55px; POSITION: absolute; TOP: 138px"
runat="server" Width="103px"
Text="Button" Height="29px" OnClick="Button_Click">
</asp:button> <asp:Label id="lblResults" runat="server"
Width="360px" Height="37px"
style="LEFT: 13px; POSITION: absolute; TOP: 203px">
</asp:Label> </form> </body> </HTML>
Run the code in Listing 7.5 and enter the value startupTable in the text box. Choose either access method and click the button: You will receive the value Customers. Next, enter a value of foo in the textbox and try this value with each method. The ConfigurationSettings.AppSettings option does not generate an error, while the AppSettingsReader method shows that an error occurred.
Creating Configuration Sections
Instead of defining many single tags, you can find that you need to persist a more complex structure. The web.config file also supports the creation of custom tags and groups by using the configSections element.
The configSections element defines the section names that appear in the configuration system. Open your machine.config file and look at the configSections section: Look familiar? This is where the sections discussed throughout this chapter are defined. Take a look at the components of the configSections section and its child elements.
The configSections element supports the following child elements:
sectionDefines a configuration section. This element is discussed in more detail in the following section.
sectionGroupDefines a grouping or namespace of configuration sections. Can contain other sectionGroup or section elements. Each sectionGroup element must have a name attribute that defines it.
removeRemoves a section or group defined higher in the configuration hierarchy. For example, if a section was defined in machine.config, it can be suppressed at the application level by using the remove element in web.config. Has the same effect as clear, but suppresses only a single element.
clearClears all predefined sections and groups. If the section or group was defined in machine.config, it can be suppressed at the application level by using the clear element. clear the same effect as remove, but it works on multiple elements at once.
section
The section defines a section within the configuration hierarchy. The section element supports the attributes listed in Table 7.10.
Table 7.10 Attributes of the section Element
Attribute Name |
Use |
Description |
name |
Required |
Specifies the name for the configuration section. |
type |
Required |
Specifies the type of section handler used to read the settings for the section. |
allowDefinition |
Optional |
Specifies which configuration file the section can be used in. Possible values are
|
allowLocation |
Optional |
Determines if the section can be used within the location element (true by default). |
In Table 7.10, one of the required attributes for the section element is the type attribute. type follows this form:
configuration section handler class, assembly [, Version, Culture, PublicKeyToken]
The configuration section handler portion of the value is any class that implements the IConfigurationSectionHandler interface. As previously mentioned, the System.Configuration namespace provides a set of classes that support this interface (DictionarySectionHandler, IgnoreSectionHandler, NameValueSection Handler, or SingleTagSectionHandler).
The assembly is the managed assembly to which the handler belongs. For example, the DictionarySectionHandler class is part of the System assembly. This can be verified by going to the Object browser and viewing the hierarchy to which the class belongs.
If the Global Assembly Cache manages the assembly, you also need to include the Version, Culture, and PublicKeyToken values. You can find these settings by going to the .NET Configuration MMC snap-in, double-clicking the assembly name, and copying the values from the dialog box. You can also determine this information by going to the .NET command prompt and running the gacutil.exe utility.
Listing 7.6 demonstrates how to define a custom group and section.
Listing 7.6 A Sample config.web File with Custom Sections Defined
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="databases"> <section name="SQLServer" type="System.Configuration.NameValueSectionHandler, System,
Version=1.0.3300.0, Culture=neutral, PublicKeyToken=
b77a5c561934e089" /> </sectionGroup> </configSections> <databases> <SQLServer> <add key="Northwind" value="User ID=sa;Password=;Initial
Catalog=Northwind;Data Source=localhost" /> <add key="Pubs" value="User ID=sa;Password=;Initial Catalog=pubs;Data
Source=localhost" /> </SQLServer> </databases> <system.web> <compilation defaultLanguage="vb" debug="true" /> </system.web> </configuration>
Backup your web.config file and save the sample file from Listing 7.6 as web.config in your local project. Then add the web form that's found in Listing 7.7.
Listing 7.7 Reading Custom Configuration Sections
<%@Import Namespace="System.Collections.Specialized"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Custom Sections Demo</title> <script language="vb" runat="server"> Private Sub Page_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Dim config As NameValueCollection =
ConfigurationSettings.GetConfig("databases/SQLServer") Response.Write("The Northwind connection string: " &
config("Northwind") & "<br>") Response.Write("The Pubs connection string: " & config("Pubs")
& "<br>") End Sub </script> </head> <body> </body> </html>
Again, make sure to check the value of the PublicKeyToken property in the type attribute to ensure that it matches the value found in your Global Assembly Cache.
System.Web.Configuration
The primary class in the System.Web.Configuration namespace is the HttpBrowserCapabilities class. As you saw in Listing 7.5, this class determines browser capabilities. Table 7.11 details the additional properties available for this class.
Table 7.11 Properties of the System.Web.HttpBrowserCapabilities Class
Property Name |
Description |
ActiveXControls |
Indicates if the browser supports ActiveX controls. |
AOL |
Determines if the user is an AOL client. |
BackgroundSounds |
Indicates if the browser supports background sounds. |
Beta |
Indicates if the browser is a beta release. |
Browser |
Gets the browser string (if any) transmitted in the User-Agent header. |
CDF |
Indicates if the browser supports Channel Definition Format (CDF) for webcasting. |
ClrVersion |
Gets the CLR version number installed on the client. |
Cookies |
Indicates if the browser supports cookies. |
Crawler |
Indicates if the browser is a web crawler search engine. |
EcmaScriptVersion |
Gets the number of the ECMA script version supported by the client. |
Frames |
Indicates if the browser supports HTML frames. |
Item |
Gets the value of the specified browser capability. |
JavaApplets |
Indicates if the browser supports Java applets. |
JavaScript |
Indicates if the browser supports JavaScript. |
MajorVersion |
Gets the major version number of the browser. |
MinorVersion |
Gets the minor version number of the browser. |
MSDomVersion |
Gets the number of the Microsoft HTML DOM version supported by the client. |
Platform |
Gets the name of the client platform. |
Tables |
Indicates if the browser supports HTML tables. |
Type |
Gets the name and major version number of the browser. |
VBScript |
Indicates if the browser supports Visual Basic Scripting (VBScript). |
Version |
Gets the full version number for the browser. |
W3CDomVersion |
Gets the number of the World Wide Web Consortium (W3C) XML DOM version supported by the client. |
Win16 |
Indicates if the client is a Win16-based computer. |
Win32 |
Indicates if the client is a Win32-based computer. |
Take a look at a practical use for storing custom configuration settings for the application and for creating custom sections. One way that this configuration system is useful is by allowing you to alter a site's look, feel, and content for different customers that use the same application.
For example, suppose that you have one base application being used by two companies simultaneously. Instead of coding the same application twice or hosting the same application in two separate locations, they can share the same set of servers. Additionally, you have the capability of moving their site content around your server without requiring extensive search and replace operations throughout the source code.
Here's a sample web.config file that uses the appSettings section to define application-wide settings (here, it is a shared database). You can also create a custom section, customers, that stores grouped information per customer. Listing 7.8 shows the necessary entries in the web.config file.
Listing 7.8 A Sample web.config File that Shows Custom Settings
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <sectionGroup name="customers"> <section name="ALFKI" type="System.Configuration.NameValueSectionHandler,
System, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"/> <section name="ANATR" type="System.Configuration.NameValueSectionHandler,
System, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" /> </sectionGroup> </configSections> <appSettings> <add key="connectionString" value="User ID=sa;Password=;Data
Source=localhost;" /> </appSettings> <customers> <ALFKI> <add key="Initial Catalog" value="Northwind" /> <add key="Styles" value="futterkiste.css" /> <add key="XSLT" value="main.xslt" /> <add key="Directory" value="Alfreds" /> </ALFKI> <ANATR> <add key="Initial Catalog" value="Northwind" /> <add key="Styles" value="trujilio.css" /> <add key="XSLT" value="layout.xslt" /> <add key="Directory" value="Trujilio" /> </ANATR> </customers> <system.web> <compilation defaultLanguage="vb" debug="true" /> </system.web> </configuration>
This web.config file contains the custom sections ALFKI and ANATR within the customers namespace. Within each one of these tags, the following keys are defined:
Initial CatalogDefines the initial catalog for the SQL Server database.
StylesDefines the cascading stylesheet filename this customer uses for site colorization.
XSLTDefines the XSLT stylesheet filename this customer uses for site layout.
DirectoryDefines the directory where the customer's files reside, relative to the current path.
You then create subdirectories for each customer that contains the files. These folder names must match the names specified in the Directory key. We also create the CSS and XSLT files specified for each customer. Listing 7.9 shows the CSS file used for customer ID ALFKI in the Northwinds database, and Listing 7.10 shows the XSLT file.
Listing 7.9 Contents of alfreds/futterkiste.css
body { color: black; font-family: Verdana, Tahoma, Sans-Serif; border-collapse: collapse; background-color: white; } .contactHeader { font-weight: bold; font-size: 10pt; color: blue; font-family: 'Comic Sans MS'; border-collapse: collapse; }
Listing 7.10 Contents of alfreds/main.xslt
<<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" encoding="ISO-8859-1"
doctype-public="-//W3C//DTD HTML 4.0 Transitional//EN" /> <xsl:param name="css" select="'styles.css'"/> <xsl:template match="/"> <link rel="stylesheet" type="text/css"> <xsl:attribute name="href">
<xsl:value-of select="$css"/>
</xsl:attribute> </link> <h1> <xsl:value-of select="Customers/CompanyName" /> </h1> <xsl:apply-templates /> <br/> <br/> <br/> <a href="choose.htm">Click here to choose again</a> </xsl:template> <xsl:template match="Customers"> <table border="0"> <tr> <th>Customer ID</th> <th>Contact Name</th> <th>Contact Title</th> <th>Phone</th> </tr> <tr> <td class="contactHeader"> <xsl:value-of select="CustomerID" /> </td> <td class="contactHeader"> <xsl:value-of select="ContactName" /> </td> <td class="contactHeader"> <xsl:value-of select="ContactTitle" /> </td> <td class="contactHeader"> <xsl:value-of select="Phone" /> </td> </tr> </table> </xsl:template> </xsl:stylesheet>
In Listing 7.10, you use a parameter for the stylesheet to dynamically locate the stylesheet file. Remember: The context page is the page that performs the transformation, so the path will be relative to this page (this is revisited in a moment).
Listing 7.11 shows the CSS file that's used for customerID ANATR, while Listing 7.12 shows the XSLT file for the customer.
Listing 7.11 Contents of trujilio/trujilio.css
body { font-size: smaller; color: black; font-family: Verdana, Tahoma, Sans-Serif; border-collapse: collapse; background-color: gray; } .contactHeader { font-weight: bold; font-size: 10pt; color: white; font-family: 'Comic Sans MS' , Verdana, Tahoma, Sans-Serif; border-collapse: collapse; }
Listing 7.12 Contents of trujilio/layout.xslt
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" encoding="ISO-8859-1" doctype-public="-
//W3C//DTD HTML 4.0 Transitional//EN" /> <xsl:param name="css" select="'styles.css'"/> <xsl:template match="/"> <link rel="stylesheet" type="text/css"> <xsl:attribute name="href">
<xsl:value-of select="$css"/>
</xsl:attribute> </link> <h1> <xsl:value-of select="Customers/CompanyName" /> </h1> <xsl:apply-templates /> <br/> <br/> <br/> <a href="choose.htm">Click here to choose again</a> </xsl:template> <xsl:template match="Customers"> <table border="0"> <tr> <td class="contactHeader">Customer ID:</td> <td><xsl:value-of select="CustomerID" /></td> </tr> <tr> <td class="contactHeader">Contact Name:</td> <td><xsl:value-of select="ContactName" /></td> </tr> <tr> <td class="contactHeader">Contact Title:</td> <td><xsl:value-of select="ContactTitle" /></td> </tr> <tr> <td class="contactHeader">Phone:</td> <td><xsl:value-of select="CustomerID" /></td> </tr> </table> </xsl:template> </xsl:stylesheet>
Now that you have seen the XSLT and CSS files associated with each customer, you can build a generic page that displays the contents for each customer based on their customer ID. Listing 7.13 lists the .aspx code that's used to generically display pages for different customers.
Listing 7.13 -WebForm2.aspx: A Generic Page Display Application that Leverages the web.config File
<%@ Import Namespace="System.Collections.Specialized"%> <%@ Import Namespace="System.Data"%> <%@ Import Namespace="System.Data.SqlClient"%> <%@ Import Namespace="System.Xml"%> <%@ Import Namespace="System.Xml.Xsl"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <title>CoBranding Sample</title> <script language="vb" runat="server"> Private Sub Page_Load(ByVal sender As System.Object, ByVal e
As System.EventArgs) Dim customerID As String customerID = Request.Form.Item("customer") Dim config As NameValueCollection =
ConfigurationSettings.GetConfig("customers/" +
customerID) Dim args As XsltArgumentList = New XsltArgumentList() args.AddParam("css", String.Empty,
config.Item("Directory") + "/" + config.Item("Styles")) Dim appReader As AppSettingsReader = New
AppSettingsReader() Dim connectionString As String =
appReader.GetValue("connectionString",
GetType(System.String)) connectionString += "Initial Catalog=" +
config.Item("Initial Catalog") Dim connection As SqlClient.SqlConnection = New
SqlClient.SqlConnection() connection.ConnectionString = connectionString Dim command As SqlClient.SqlCommand = New
SqlClient.SqlCommand() connection.Open() command.Connection = connection command.CommandText = "SELECT * FROM Customers WHERE
CustomerID='" + customerID + "' FOR XML AUTO,ELEMENTS" command.CommandType = CommandType.Text Dim reader As XmlTextReader reader = command.ExecuteXmlReader() Dim strXML As String Do While reader.Read() strXML += reader.ReadOuterXml() Loop reader.Close() connection.Close() connection.Dispose() command.Dispose() Xml1.TransformArgumentList = args Xml1.DocumentContent = strXML Xml1.TransformSource = config.Item("Directory") + "/" +
config.Item("XSLT") End Sub </script> </HEAD> <body MS_POSITIONING="GridLayout"> <form id="Form1" method="post" runat="server"> <asp:xml id="Xml1" runat="server" /> </form> </body> </HTML>
Let's walk through the sample application. When the page is loaded, the Page_Load event is fired. You retrieve the customer ID value from the customer form element and use that value as the basis for retrieving the custom configuration section. You then read from that section to find the CSS file location and add that value as a parameter for the XSLT transformation. The database connection string is read from the appSettings section, and the customer's individual initial catalog is applied to the connection string.
XML is retrieved from SQL Server using the FOR XML clause (see Chapter 9, "SQL Server 2000 and XML," for more information on the FOR XML clause). The returned XML string is used as the source for the <asp:xml> control on the form, and the list of parameters is added to the control. Finally, you locate the XSLT file for the particular customer, and the page is displayed.
To test the application, build a simple driver HTML page to call the test application. Listing 7.14 shows the sample driver page.
Listing 7.14 Choose.htm: A Simple Driver Page Used to Test the Application
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Choose Customer</title> </head> <body MS_POSITIONING="GridLayout"> <form action="webform2.aspx" method="post"> <select name="customer"> <option value="ALFKI">Alfreds Futterkiste</option> <option value="ANATR">Ana Trujilio</option> </select> <br/> <input type="submit" value="Load CoBranding"> </form> </body> </html>
This sample driver file simply posts the customer ID to the page described in Listing 7.13. Compare the results in Figures 7.3 and 7.4 to see that the XSLT file is applied differently per customer ID.
Figure 7.3 Using customer ID ALFKI, you can apply the XSLT and CSS files to the XML data for the customer based on the settings in the web.config file.
Figure 7.4 Viewing data for customer ID ANATR displays the same data, but using a different stylesheet and CSS file as specified in the web.config