![]() ![]() |
20.3 Configuring the ApplicationASP.NET provides a very powerful and flexible means of configuring applications. This configuration is accomplished using text-based XML configuration files. The server-wide configuration file is called machine.config, described in Section 20-3.1. This is supplemented by a number of application-specific configuration files, all called web.config, located in the application virtual root directory and subdirectories. This configuration scheme offers the following features:
20.3.1 Hierarchical ConfigurationThe configuration system is hierarchical, just as a directory tree structure is hierarchical. The file at the very top of the hierarchy is a file called machine.config. This file is contained in the subdirectory: c:\winnt\Microsoft.NET\Framework\version number\CONFIG where version number will be replaced with the version of the .NET runtime installed on your machine, such as v1.1.4322. All the other configuration files are called web.config. These files are optional: if there are none anywhere in an application virtual directory or its subdirectories, then the configuration settings contained in machine.config will apply to your application without any modifications. Each directory and subdirectory contained in the application can have at most a single web.config file. The configuration settings contained in a specific instance of web.config apply to the directory in which it is contained and to all its child directories. If a specific instance of web.config contains a setting that is in conflict with a setting higher up in the configuration hierarchy (i.e., in a parent directory or machine.config), then the lower-level setting will override and apply to its own directory and all child subdirectories below it (unless, of course, any of those child subdirectories have their own copies of web.config, which will further override the settings). So, for example, consider the directory structure shown in Figure 20-5. The virtual root of the web site is called MyWebSite, corresponding to the physical directory c:\inetpub\wwwroot\MyWebSite. Underneath the virtual root are two child subdirectories, each of which has additional child subdirectories. The URL for this web site would be www.MyWebSite.com (assuming that the domain name MyWebSite.com was registered to the IP address assigned to the server). Figure 20-5. Hierarchical configuration![]() If there were no web.config files in any of these directories, then all the configuration would come directly from machine.config. If there is a version of web.config in the directory MyWebSite, then any settings it contains would apply to the entire application (but only to that application), including all the subdirectories beneath it. If there were another version of web.config in the MembersOnly directory, then its configuration settings would apply to the MembersOnly directory and its subdirectories, but not to PublicStuff. If any of the settings in web.config in MembersOnly conflicted with those in MyWebSite, then the settings in MembersOnly would override those in MyWebSite. It is important to note that the hierarchical nature of the configuration files is based on application virtual directories. Refer again to Figure 20-5. The only virtual directory defined so far for that application is MyWebSite. However, suppose another virtual directory, MyPublicWebSite, were defined, corresponding to c:\inetpub\wwwroot\MyWebSite\PublicStuff. The URL for this application would be www.MyPublicWebSite.com. This application would inherit the configuration settings from machine.config, but not from c:\inetpub\wwwroot\MyWebSite\web.config. Although c:\inetpub\wwwroot\MyWebSite is the physical parent directory of c:\inetpub\wwwroot\MyWebSite\PublicStuff, it is not the virtual parent. In fact, c:\inetpub\wwwroot\MyWebSite\PublicStuff is a virtual root and does not have a parent. Configuration settings inherit from virtual parents, not physical parents. 20.3.2 FormatThe configuration files, machine.config and web.config, are XML files. As such they must be well-formed. (For a description of well-formed XML, see the sidebar "Well-Formed HTML" in Chapter 4.) Specifically, these files consist of a nested hierarchy of XML tags. All opening tags must have the corresponding closing tag or be self-closing (with a trailing / character just inside the closing angle bracket). The tag pairs must not be interleaved with other tag pairs. Subtags may be nested inside tag pairs. Both tags and subtags may have attributes and attribute values. All of these elements are case-sensitive. Typically, tag and attribute names consist of one or more words run together. Tag and attribute names are camel-cased. Attribute values are usually Pascal-cased. Camel-casing means that all the characters are lowercase, including the first character, except the first character of each run-on word after the first. Examples of camel-casing are appSettings, configSections, section, and sessionState.
The word usually is used because there are exceptions:
The first line in the configuration file declares the file to be an XML file, with attributes specifying the version of the XML specification to which the file adheres and the character encoding used. Here is a typical XML declaration line: <?xml version="1.0" encoding="UTF-8" ?> The character encoding specified here is UTF-8, which is a superset of ASCII. The character encoding parameter may be omitted if, and only if, the XML document is written in either UTF-8 or UTF-32. Therefore, if the XML file is written in pure ASCII, the encoding parameter may be omitted, although including the attribute contributes to self-documentation. The next line in the configuration files is the opening <configuration> tag: <configuration> The entire contents of the configuration file, except the initial XML declaration, is contained between the opening <configuration> tag and the closing </configuration> tag. Comments can be contained within the file using the standard XML (and HTML) format: <!-- Your comments here --> Within the <configuration> tags are two broad categories of entries. They are, in the order in which they appear in the configuration files:
20.3.3 Configuration Section Handler DeclarationsThe handler declarations are contained between an opening <configSections> tag and a closing </configSections> tag. Each handler declaration specifies the name of a configuration section, contained elsewhere in the file that provides specific configuration data. Each declaration also contains the name of the .NET class that will process the configuration data in that section.
The machine.config file contains, in the default installation, many configuration section handler declarations that cover the areas subject to configuration by default. (Since this is an extensible system, you can also create your own. A typical entry containing a handler declaration is shown in Example 20-12.
Example 20-12. Typical configuration section handler declaration<section name="compilation" type="System.Web.UI.CompilationConfigurationHandler, System.Web, Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> Despite appearances to the contrary, the <section> tag has only two attributes: name and type. The name is compilation. This implies that somewhere else in the configuration file is a configuration section called compilation. This configuration section will contain the configuration settings, which are name/value pairs, to be used by the application(s). It will be described in detail shortly. The type attribute has a lengthy parameter enclosed in quotation marks. This parameter contains:
Each handler need only be declared once, either in the base level machine.config file or in a web.config file further down the configuration hierarchy. The configuration section it refers to can then be specified as often as desired in other configuration files. Example 20-13 shows a truncated version of the default machine.config.
Example 20-13. Truncated machine.config file<?xml version="1.0" encoding="UTF-8" ?> <configuration> <configSections> <section name="runtime" type="System.Configuration.IgnoreSectionHandler" /> <section name="mscorlib" type="System.Configuration.IgnoreSectionHandler" /> <section name="startup" type="System.Configuration.IgnoreSectionHandler" /> <section name="appSettings" type="System.Configuration.NameValueFileSectionHandler " /> <sectionGroup name="system.net"> <section name="defaultProxy" type="System.Net.Configuration.DefaultProxyHandler " /> </sectionGroup> <sectionGroup name="system.web"> <section name="compilation" type="System.Web.UI.CompilationConfigurationHandler " /> <section name="pages" type="System.Web.UI.PagesConfigurationHandler " /> </sectionGroup> </configSections> <appSettings> <!-- use this section to add application specific configuration example: <add key="XML File Name" value="myXmlFileName.xml" /> --> </appSettings> <system.net> <defaultProxy> <proxy usesystemdefault="true" /> </defaultProxy> <webRequestModules> <add prefix="http" type="System.Net.HttpRequestCreator" /> <add prefix="https" type="System.Net.HttpRequestCreator" /> <add prefix="file" type="System.Net.FileWebRequestCreator" /> </webRequestModules> </system.net> <system.web> <compilation debug="false" explicit="true" defaultLanguage="vb"> <compilers> <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CSharp.CSharpCodeProvider " /> <compiler language="vb;visualbasic;vbscript" extension=".vb" type="Microsoft.VisualBasic.VBCodeProvider " /> <compiler language="js;jscript;javascript" extension=".js" type="Microsoft.JScript.JScriptCodeProvider " /> <compiler language="VJ#;VJS;VJSharp" extension=".jsl" type="Microsoft.VJSharp.VJSharpCodeProvider "/> </compilers> <assemblies> <add assembly="mscorlib"/> <add assembly="System "/> <add assembly="System.Web "/> <add assembly="System.Data "/> <add assembly="System.Web.Services "/> <add assembly="System.Xml "/> <add assembly="System.Drawing "/> <add assembly="*"/> </assemblies> </compilation> <pages buffer="true" enableSessionState="true" enableViewState="true" enableViewStateMac="false" autoEventWireup="true" validateRequest="true" /> </system.web> </configuration> The first three declarations in machine.config are runtime, mscorlib, and startup. They are special because they are the only declarations that do not have corresponding configuration sections in the file. In Example 20-13, you can also see that many of the handler declarations are contained within <sectionGroup> tags. The name attribute of these tags corresponds to the namespace that contains the handlers. This groups together all the configuration sections that are handled out of the same namespace. 20.3.4 Configuration SectionsThe configuration sections contain the actual configuration data. They each are contained within a pair of tags corresponding to the name of the section specified in the configuration section handler declaration. Alternatively, a single self-closing tag can be used. For example, the following two configuration sections are equivalent: <globalization requestEncoding="utf-8" responseEncoding="utf-8" /> and: <globalization> requestEncoding="utf-8" responseEncoding="utf-8" </globalization> Configuration sections contain name/value pairs that hold the configuration data. They may also contain subsections. machine.config contains one configuration section for each handler declaration. If the handler declaration was contained within a <sectionGroup> tag, then its corresponding configuration section will be contained within a tag containing the name of the <sectionGroup>. This can be seen in Example 20-13 for both system.net and system.web. The sections that follow provide a description of each of the configuration sections contained in the default machine.config. There are other configuration sections that are beyond the scope of this book, including system. diagnostics, system.runtime.remoting, and system. windows.forms. 20.3.4.1 appSettingsappSettings allows you to easily store application-wide name/value pairs for read-only access. It is similar in function to application objects in the global.asax file. Consider the handler declaration for appSettings, shown in Example 20-13 and reproduced here: <section name="appSettings" type="System.Configuration.NameValueSectionHandler " /> This indicates that the NameValueSectionHandler class is used to handle appSettings. This class provides name/value pair configuration handling for a specific configuration section. As seen in Example 20-13, the appSettings section in the default machine.config file contains only a comment. More typically, you would add an appSettings section to one or more web.config files. Example 20-14 shows a web.config file with an appSettings section added to provide two application-wide values. Note that the appSettings section is not contained within any higher-level tag other than <configuration>. Example 20-14. appSettings configuration section<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="appDSN" value=" SERVER=Zeus;DATABASE=Pubs;UID=sa;PWD=secret;" /> <add key="appTitle" value="Programming ASP.NET" /> </appSettings> </configuration> These values can be accessed anywhere in the application to which this configuration is applicable (i.e., its current directory and any child directories in which the value is not overridden by another web.config file). Example 20-15 and Example 20-16 show a script block from an .aspx file to illustrate how this is done in both VB.NET and C#, respectively. The C# version of the code in Example 20-16 shows only the script block, since the HTML is identical to the VB.NET version in Example 20-15. Example 20-15. Reading appSettings values in VB.NET<%@ Page Language="vb" %> <script runat="server"> sub Page_Load(ByVal Sender as Object, _ ByVal e as EventArgs) if not IsPostBack then dim strDSN as string strDSN = ConfigurationSettings.AppSettings("appDSN") ' use the DSN to connect to the database here lblDSN.Text = strDSN lblTitle.Text = ConfigurationSettings.AppSettings("appTitle") end if end sub </script> <html> <body> <form runat="server"> <h1>Configuration</h1> Application DSN: <asp:Label id="lblDSN" runat="server"/> <br/> Application Title: <asp:Label id="lblTitle" runat="server"/> </form> </body> </html> Example 20-16. Reading appSettings values in C#<%@ Page Language="cs" %> <script runat="server"> void Page_Load(Object Source, EventArgs E) { if (!IsPostBack) { string strDSN; strDSN = ConfigurationSettings.AppSettings["appDSN"]; // use the DSN to connect to the database here lblDSN.Text = strDSN ; lblTitle.Text = ConfigurationSettings.AppSettings["appTitle"]; } } </script> Configuration settings are read by an application using the AppSettings property of the ConfigurationSettings class. This class provides methods and properties for reading configuration settings in an application's configuration files. It is part of the System.Configuration namespace, which is automatically imported into every ASP.NET application. The AppSettings property of the ConfigurationSettings class is of type NameValueCollection. It takes a key as a parameter and returns the value associated with that key. 20.3.4.2 system.netThe system.net configuration section contains subsections that deal with networking. These subsections include authenticationModules, defaultProxy, connectionManagement, and webRequestModules. These subsections are not used by ASP.NET developers and are outside the scope of this book. 20.3.4.3 system.webThe system.web configuration section contains subsections that configure ASP.NET. Each of these subsections will be described briefly in the following sections. browserCapsThis subsection contains information about the capabilities of all the web browsers and operating systems your clients are likely to use. This information includes such items as the name of the browser; its major and minor version numbers; whether it supports frames, tables, cookies, cascading style sheets, VBScript, JavaScript, Java applets; and so on. The version of <browserCaps> contained in the default machine.config file performs fairly extensive testing of the client browser to determine both the browser capabilities and the client platform. As new browser versions come on the market, you can update the information contained in this section by visiting http://www.cyscape.com/browsercaps/. clientTargetClosely related to <browserCaps>, the <clientTarget> subsection provides ASP.NET with aliases for the browsers. For example, it provides the aliases shown in Table 20-2.
compilationThis subsection allows you to configure the compilation behavior in ASP.NET. For example, you can change the default language and enable or disable debugging. If the default language is VB.NET, you can also set the explicit attribute to true, which is the equivalent of including the Option Explicit On statement in your page or web service. The default <compilation> tag in machine.config looks like this: <compilation debug="false" explicit="true" defaultLanguage="vb"> In a VB.NET project created in Visual Studio .NET, the default <compilation> tag in the web.config file for the application looks like this: <compilation defaultLanguage="vb" debug="true" /> For a C# project in Visual Studio .NET, it looks like this: <compilation defaultLanguage="c#" debug="true" /> Notice that the debug attribute in either web.config file overrides the value set in machine. config. In addition to setting the language and debug mode, this subsection includes two other subsections: <compilers> and <assemblies>. The former subsection specifies what language names map with what file extensions. It also specifies the class containing the code provider and version information. The latter subsection specifies which assembly files are to be included when the project is compiled. pagesThis subsection specifies whether page options (such as buffering, session state, and view state) are enabled for the pages under the control of the configuration file. The default <pages> tag in the default machine.config file looks like this: <pages buffer="true" enableSessionState="true" enableViewState="true" enableViewStateMac="false" autoEventWireup="true" /> The autoEventWireup attribute is specific to Visual Studio .NET. When AutoEventWireup is true (the default value outside of VS.NET), then the .NET Framework calls page events (Page_Init & Page_Load) automatically, with no code necessary to explicitly add an event handler to an event delegate. If AutoEventWireup is false (the default when using VS.NET), you must explicitly add event handler methods to the event delegates. VS.NET does this for you. customErrorsThis subsection allows you to control what the user sees when there is an error. Example 20-17 shows a typical <customErrors> configuration section that demonstrates the available features. Example 20-17. <customErrors> configuration section<customErrors defaultRedirect="StdError.htm" mode="RemoteOnly" > <error statusCode="404" redirect="err404.htm" /> <error statusCode="407" redirect="err407.htm" /> </customErrors > When custom errors are enabled, if an error occurs, the web page specified in defaultRedirect is presented to the client rather than the standard ASP.NET error page. The mode attribute specifies how custom errors are enabled. There are three possible values for this mode, which are shown in Table 20-3.
You can add multiple <error> tags to present specific error pages for specific errors. In Example 20-17, error 404 will result in err404.htm being presented to the client, error 407 will result in err407.htm, and all other errors will result in StdError.htm being presented. In any case, the developer working on the local machine will see none of these custom error pages, but rather will see the standard error page put up by ASP.NET. httpRuntimeThis subsection configures the ASP.NET HTTP runtime settings. There are several attributes available in this section, as shown in Table 20-4.
The <httpRuntime> tag in the default machine.config file looks like this: <httpRuntime executionTimeout="90" maxRequestLength="4096" useFullyQualifiedRedirectUrl="false" minFreeThreads="8" minLocalRequestFreeThreads="4" appRequestQueueLimit="10" /> globalizationThis subsection is used to configure the globalization settings for an application. The attributes shown in Table 20-5 are supported.
The <globalization> tag in the default machine.config file looks like this: <globalization requestEncoding="utf-8" responseEncoding="utf-8" /> httpHandlersThis subsection maps incoming requests to a class that implements either the IHttpHandler or IHttpHandlerFactory interfaces. There is a fairly extensive mapping in the default machine.config file, which maps standard file types to a specific class (e.g., all .aspx requests are mapped to the PageHandlerFactory class). The <httpHandlers> tag has several subtags:
httpModulesThis subsection configures the HTTP modules within an application. Each <add> subtag within the subsection assigns a class to a module. The default machine.config file includes the modules and their classes shown in Table 20-6.
processModelThis tag configures the process model settings on an IIS web server. The <processModel> tag in the default machine.config file looks like this: <processModel enable="true" timeout="Infinite" idleTimeout="Infinite" shutdownTimeout="0:00:05" requestLimit="Infinite" requestQueueLimit="5000" restartQueueLimit="10" memoryLimit="60" webGarden="false" cpuMask="0xffffffff" userName="machine" password="AutoGenerate" logLevel="Errors" clientConnectedCheck="0:00:05" comAuthenticationLevel="Connect" comImpersonationLevel="Impersonate" /> Two of these attributes are worth special mention, since they can improve the stability of the system. The timeout attribute controls how often every process is automatically shut down and restarted. The default value is "Infinite," indicating that the process is never restarted. It may also have a time value of the form hh:mm:ss. So, for example, if you want every process to restart every 20 minutes, use the value "00:20:00." The requestLimit attribute specifies how many requests each process can receive before it is re-started. The default value is also "Infinite." Typical values would be an integer inside quotes. So, to restart every process every 5000 requests, use the value "5000." For a detailed description of each of these attributes, consult the SDK documentation. Note that these settings are ignored if you are using IIS 6, which has its own process model settings. sessionStateThe <sessionState> tag configures session state. Chapter 6 fully examines session state. This tag supports the attributes shown in Table 20-7.
The default <sessionState> tag in machine.config is shown here: <sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" stateNetworkTimeout="10" sqlConnectionString="data source=127.0.0.1;user id=sa;password=" cookieless="false" timeout="20" /> traceThe <trace> tag configures the ASP.NET trace service. Chapter 7 describes tracing fully. The <trace> tag supports the attributes shown in Table 20-8.
The default <trace> tag in machine.config is shown here: <trace enabled="false" localOnly="true" pageOutput="false" requestLimit="10" traceMode="SortByTime" /> webControlsThe <webControls> tag specifies the location of the script that is generated to be run client-side. It supports a single attribute, clientScriptsLocation. The default <webControls> tag in machine.config is shown here: <webControls clientScriptsLocation="/aspnet_client/{0}/{1}/" /> webServicesThe <webServices> tag configures web services. The default <webServices> tag in machine.config is shown here: webServices> <protocols> <add name="HttpSoap"/> <add name="HttpPost"/> <add name="HttpGet"/> <add name="Documentation"/> </protocols> <soapExtensionTypes> </soapExtensionTypes> <soapExtensionReflectorTypes> </soapExtensionReflectorTypes> <soapExtensionImporterTypes> </soapExtensionImporterTypes> <wsdlHelpGenerator href="DefaultWsdlHelpGenerator.aspx" /> <serviceDescriptionFormatExtensionTypes> </serviceDescriptionFormatExtensionTypes> /webServices> 20.3.4.4 Security settingsMany aspects of ASP.NET security are configurable, using the machine.config and web.config files. For a complete discussion of the security concepts configured here, see Chapter 19. There are several configuration sections controlling security. They are described in the following sections. identityThe <identity> tag controls the identity of the application at runtime. Specifically, it enables and disables impersonation, and if impersonation is enabled, it allows you to specify the userName and password to use. The <identity> tag supports three attributes shown in Table 20-9.
The <identity> tag in the default machine.config file looks like this: <identity impersonate="false" userName="" password=""/> authenticationThe <authentication> tag controls authentication in ASP.NET applications. As is described fully in Chapter 19, authentication is the process whereby ASP.NET security verifies that a client making a request is who they say they are. The <authentication> tag has one attribute, mode, which specifies the default authentication mode for the application. There are four legal values for mode, which are shown in Table 20-10.
The <authentication> tag also has two subtags. They are <forms> and <passport>. The <forms> tag has five attributes, listed in Table 20-11.
The <forms> tag also has one subtag, <credentials>. This subtag allows you to specify the type of password encryption used and also to define name/password pairs within the <user> subtag. The <credentials> tag has a single attribute, passwordFormat. This attribute has three legal values, which are shown in Table 20-12.
The <credentials> tag enables you to specify user/password pairs using the <user> subtag. The <user> subtag has two attributes: name and password. Their values are the username and password, respectively. The <passport> subtag of the <authentication> tag has a single attribute, redirectUrl. The value of this attribute is the URL to redirect to if the page requires authentication and the user has not signed on with Passport. The <authentication> tag in the default machine.config file looks like this: <authentication mode="Windows"> <forms name=".ASPXAUTH" loginUrl="login.aspx" protection="All" timeout="30" path="/" > <credentials passwordFormat="SHA1"> <!-- <user name="UserName" password="password"/> --> </credentials> </forms> <passport redirectUrl="internal" /> </authentication> authorizationThe <authorization> tag controls authorization in ASP.NET applications. Authorization is how ASP.NET security controls access to URL resources. The <authorization> tag supports two subtags, <allow> and <deny>. Both subtags have the same set of three attributes, which are shown in Table 20-13. Those attributes are used to define access rules that are iterated at runtime. Access for a particular user is allowed or denied based on the first rule found that fits that user.
The default <authorization> tag in machine.config is shown here. It allows all users. <authorization> <allow users="*" /> </authorization> machineKeyThe <machineKey> tag configures keys used for encryption and decryption of authentication cookies. This section can be declared at the server level in machine.config or in web.config files at the site or application root level. The <machineKey> tag supports three attributes, which are shown in Table 20-14.
The default authorization in machine.config is shown here: <machineKey validationKey="AutoGenerate" decryptionKey="AutoGenerate" validation="SHA1"/> securityPolicyThe <securityPolicy> tag maps named security levels to policy files. This section can be declared at the server level in machine.config or in web.config files at the site or application root level. The <securityPolicy> tag supports one subtag, <trustLevel>. This subtag is used to specify one security level name and an associated policy level. There is a separate <trustLevel> tag for each named security level. The <trustLevel> tag supports the two attributes shown in Table 20-15.
The default <securityPolicy> in machine.config is shown here: <securityPolicy> <trustLevel name="Full" policyFile="internal" /> <trustLevel name="High" policyFile="web_hightrust.config" /> <trustLevel name="Low" policyFile="web_lowtrust.config" /> <trustLevel name="None" policyFile="web_notrust.config" /> </securityPolicy> trustThe <trust> tag configures the code access security permissions for an application. This section can be declared at the server level in machine.config or in web.config files at the site or application root level. The <trust> tag supports the two attributes shown in Table 20-16.
The default <trust> in machine.config is shown here: <trust level="Full" originUrl="" /> locationThe location section is used to apply configuration settings to specific resources, such as individual web pages or contained subdirectories. The <location> tag has a single attribute, path. The path attribute specifies a file or child directory (relative to the location of the current web.config file) to which specific configuration settings apply. Suppose you had an application with custom error pages specified in the web.config file in the application virtual root directory. These custom error pages would apply to the entire application, including all child directories. Suppose further that there are two subdirectories under the virtual root directory, called sub1 and sub2. sub1 is to have the application-wide custom error handling, but sub2 is to have its own specific error handling. You could put another copy of web.config in sub2 to override the custom error handling, but an alternative would be to use the <location> tag. You would add the following lines to the web.config file in the virtual root of the application: <location path="sub2"> <system.web> <customErrors defaultRedirect="Sub2Error.htm" mode="RemoteOnly" > <error statusCode="404" redirect="err404-sub2.htm" /> <error statusCode="407" redirect="err407-sub2.htm" /> </customErrors > </system.web> </location> Notice that the <system.web> tag must be reproduced within the location section. The configuration settings contained in a location section will apply to the directory specified in the path attribute and also to any child directories of that directory, unless they are further overridden either by another web.config file or another location section. If you want to apply specific configuration settings to a single file, that too can be done using a location section. Suppose the application root had a web page that requires special error handling. The following location section will accomplish that. <location path="SpecialPage.aspx"> <system.web> <customErrors defaultRedirect="SpecialError.htm" mode="RemoteOnly" > <error statusCode="404" redirect="err404-spcl.htm" /> </customErrors > </system.web> </location> 20.3.5 Custom ConfigurationIn addition to all the predefined configuration sections, you can also add your own custom configuration sections. There are two different types of custom configuration sections you might wish to add:
Both will be demonstrated here. 20.3.5.1 Name/value pairsBack in Example 20-14, you added an <appSettings> key to store the database DSN string. Suppose you wanted to store DSNs for multiple databases, say one called Test (for testing purposes) and one called Content (to hold the production content). A custom configuration section returning a name/value pair would be one way to handle this situation. The finished version of lines of code inserted into web.config is shown in Example 20-18. There are several steps to adding a custom configuration section that returns a name/value pair:
Example 20-18. Custom sections in web.config<configSections> <section name="altDB" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0, Culture=neutral,PublicKeyToken=b77a5c561934e089, Custom=null" /> </configSections> <altDB> <add key="Test" value=" SERVER=Zeus;DATABASE=Test;UID=sa;PWD=secret;" /> <add key="Content" value=" SERVER=Zeus;DATABASE=Content;UID=sa;PWD=secret;" /> </altDB> Note that the type in the <section> tag is exactly the same as that provided for appSettings in the machine.config file. It specifies the NameValueSectionHandler class in the System.dll assembly file. For further documentation, check SDK documentation, search on Custom Elements and choose Custom Element for NameValueSectionHandler. To read the contents of this custom configuration section, you again use a method from the ConfigurationSettings class, this time the GetConfig method. The code for a sample web page for doing this is shown in Example 20-19 in VB. NET and in Example 20-20 in C#. The C# version of the code in Example 20-20 shows only the script block, since the HTML is identical to the VB.NET version in Example 20-19. Example 20-19. Reading custom configuration values in VB.NET, vbConfig-02.aspx<%@ Page Language="vb" %> <script runat="server"> sub Page_Load(ByVal Sender as Object, _ ByVal e as EventArgs) if not IsPostBack then dim strTest as string dim strContent as string strTest = ConfigurationSettings.GetConfig("altDB")("Test") lblTest.Text = strTest lblContent.Text = _ ConfigurationSettings.GetConfig("altDB")("Content") end if end sub </script> <html> <body> <form runat="server"> <h1>Configuration</h1> <b>Test Database DSN: </b> <asp:Label id="lblTest" runat="server"/> </br> <b>Content Database DSN: </b> <asp:Label id="lblContent" runat="server"/> </form> </body> </html> Example 20-20. Reading custom configuration values in C#, csConfig-02.aspx<%@ Page Language="cs" %> <script runat="server"> void Page_Load(Object Source, EventArgs E) { if (!IsPostBack) { string strTest; string strContent; strTest = ((NameValueCollection) ConfigurationSettings.GetConfig("altDB"))["Test"]; lblTest.Text = strTest; lblContent.Text = ((NameValueCollection) ConfigurationSettings.GetConfig("altDB"))["Content"]; } }</script> The code in Example 20-19 and Example 20-20 shows two equivalent ways of displaying the contents of the key value. One way is to assign the value to a string, then assign the string to the Text property of a label. The other way is to assign the value directly to the Text property. Although the latter technique is more concise, the former is often easier to debug. In either case, notice the highlighted code in Example 20-19 and Example 20-20. These are the calls to the GetConfig method. They are different for VB.NET and C#, and a bit confusing in both. The GetConfig method takes a configuration section name as a parameter and returns an object of type NameValueCollection. The desired value in the collection is retrieved by using the key as an offset into the collection, using the get property syntax. In VB.NET, a property is retrieved by enclosing the property name in parentheses, and in C#, the property is retrieved using square brackets. Notice that the C# code first casts, or converts, the value returned by GetConfig to type NamedValueCollection, while VB.NET does not. This is because C# does not support late binding, while VB.NET does by default. You can disable late binding in VB.NET (almost always a smart move) by setting the Strict attribute to true in the Page directive. You must then explicitly cast the object returned by GetConfig, just as in C#. This is shown in Example 20-21. Example 20-21. Reading custom configuration values in VB.NET using early bindingvbConfig-02b.aspx<script runat="server"> sub Page_Load(ByVal Sender as Object, _ ByVal e as EventArgs) if not IsPostBack then dim strTest as string dim strContent as string strTest = _ CType(ConfigurationSettings.GetConfig("altDB"), _ NameValueCollection)("Test") lblTest.Text = strTest lblContent.Text = _ CType(ConfigurationSettings.GetConfig("altDB"), _ NameValueCollection)("Content") end if end sub </script> 20.3.5.2 ObjectsappSettings and custom configuration sections are very useful. However, they both suffer from the same limitation of only being able to return a name/value pair. Sometimes it would be very useful to return an object. For example, suppose you have a standard query into a database. You could store the query string in an appSettings tag, then open a database connection after retrieving the string. However, it would be much more convenient to store the query string in web.config and then have the configuration system return a DataSet directly. To do this, you must add a <section> tag and a configuration section to the designated configuration file, just as with the custom section returning name/value pairs, described in the previous section. Edit the web.config file used in the previous example and shown in Example 20-18, adding the lines of code highlighted in Example 20-22. Example 20-22. Custom sections returning objects in web.config<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="altDB" type="System.Configuration.NameValueSectionHandler, System" /> <sectionGroup name="system.web"> <section name="DataSetSectionHandler" type="ProgAspNet.Handlers.DataSetSectionHandler, vbSectionHandlers"> </section> </sectionGroup> </configSections> <appSettings> <add key="appDSN" value=" SERVER=Zeus;DATABASE=Pubs;UID=sa;PWD=secret;" /> <add key="appTitle" value="Programming ASP.NET" /> </appSettings> <altDB> <add key="Test" value=" SERVER=Zeus;DATABASE=Test;UID=sa;PWD=secret;" /> <add key="Content" value=" SERVER=Zeus;DATABASE=Content;UID=sa;PWD=secret;" /> </altDB> <system.web> <!-- Custom config section returning an object --> <DataSetSectionHandler str="Select BugID, Description from Bugs" /> </system.web> </configuration> In the <sectionGroup> section within the <configSections> section, a handler declaration is created for the DataSetSectionHandler within the system.web group. This specifies that elsewhere within the file, there will be a custom configuration section called DataSetSectionHandler within the system.web custom section. Furthermore, it also specifies that the class that will handle that configuration section is called ProgAspNet. Handlers.DataSetSectionHandler, and that the class will be found in an assembly file called vbSectionHandlers.dll in the \bin directory. Further down in the file, within the <system.web> section, there is in fact a section called DataSetSectionHandler. It has a single attribute, str. This is a string containing the SQL statement you wish to pass to the database. Next you must create the ProgAspNet.Handlers.DataSetSectionHandler class and place it in a file called DataSetSectionHandler.vb. To do this, create a VB.NET source code file as shown in Example 20-23. (The C# version of the same class is shown in Example 20-24.) Example 20-23. Source code for section handler in VB.NET, DataSetSectionHandler.vbImports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Xml
Imports System.Configuration
Namespace ProgAspNet.Handlers
public class DataSetSectionHandler
Implements IConfigurationSectionHandler
public Function Create(parent as Object, _
configContext as Object, _
section as XmlNode) as Object _
Implements IConfigurationSectionHandler.Create
dim strSql as string
strSql = section.Attributes.Item(0).Value
dim connectionString as string = "server=Ath13; uid=sa; " & _
"pwd=password; database=Bugs"
' create the data set command object and the DataSet
dim da as SqlDataAdapter = new SqlDataAdapter(strSql, _
connectionString)
dim dsData as DataSet = new DataSet( )
' fill the data set object
da.Fill(dsData,"Bugs")
return dsData
end Function
end class
end NameSpace
Example 20-24. Source code for section handler in C#, DataSetSectionHandler.csusing System;
using System.Data;
using System.Data.SqlClient;
using System.Xml;
using System.Configuration;
namespace ProgAspNet.Handlers
{
public class DataSetSectionHandler : IConfigurationSectionHandler
{
public Object Create(Object parent,
Object configContext,
XmlNode section)
{
string strSql;
strSql = section.Attributes.Item(0).Value;
string connectionString = "server=Ath13; uid=sa; " +
"pwd=password; database=Bugs";
// create the data set command object and the DataSet
SqlDataAdapter da = new SqlDataAdapter(strSql,
connectionString);
DataSet dsData = new DataSet( );
// fill the data set object
da.Fill(dsData,"Bugs");
return dsData;
}
}
}
The database aspects of the code in this example are covered thoroughly in Chapter 11 and won't be covered here in detail. At the beginning of the Example 20-23 are several Imports statements (when written in C#, these are using statements). Next a namespace is declared to contain the class. This is to prevent any ambiguity when calling the class. In order for a class to be used as a configuration section handler, it must be derived from the IConfigurationSectionHandler interface. In VB.NET, this is implemented by using the Implements keyword. (In C#, this would be indicated with a colon between the class or method name and the class or interface being inherited.)
The IConfigurationSectionHandler interface has only a single method, Create. Therefore our implementing class must implement the Create method with the specified signature. The three parameters are dictated by the interface. The first two parameters are rarely used and will not be further discussed here. The third parameter is the XML data from the configuration file. The XML node is parsed and the value of the first item in the Attributes collection is assigned to a string variable in this line: strSql = section.Attributes.Item(0).Value Once the SQL string is in hand, the connection string is hard-coded, a SqlDataAdapter object is instantiated and executed, and the DataSet is filled. Then the DataSet is returned. Before this class can be used it must be compiled. Open a command
prompt by clicking on the Start button, then Microsoft Visual Studio
.NET vbc /t:library /out:bin\vbSectionHandlers.dll /r:system.dll,System.data.dll,System.xml.dll DataSetSectionHandler.vb In C#, use the following: csc /t:library /out:bin\vbSectionHandlers.dll /r:system.dll,System.data.dll,System.xml.dll DataSetSectionHandler.cs Using command-line compilers is explained in some detail in Chapter 16. Here the target type of output is set to be library, i.e., a dll. The name of the output file to be placed in the bin directory will be vbSectionHandlers.dll. Notice that three dll files are referenced. The input source file is DataSetSectionHandler.vb. When the source file is compiled, you will have the output dll in the \bin directory, where the classes it contains will automatically be available to the application. The web page shown in Example 20-25 (in VB.NET) and Example 20-26 (in C#) shows how to utilize this configuration section. Example 20-25. Section handler demonstration in VB.NET, vbConfig-03.aspx<%@ Page Language="vb" %>
<%@ Import namespace="System.Data" %>
<script runat="server">
sub Page_Load(ByVal Sender as Object, _
ByVal e as EventArgs)
if not IsPostBack then
CreateDataGrid( )
end if
end sub
sub CreateDataGrid( )
dim dsGrid as new DataSet
dsGrid = _
ConfigurationSettings.GetConfig( _
"system.web/DataSetSectionHandler")
dg.DataSource=dsGrid.Tables(0)
dg.DataBind( )
end sub
</script>
<html>
<body>
<form runat="server">
<h1>Configuration</h1>
<asp:DataGrid
id="dg"
runat="server"/>
</form>
</body>
</html>
Example 20-26. Section handler demonstration in C#, csConfig-03.aspx<%@ Page Language="C#" %> <%@ Import namespace="System.Data" %> <script runat="server"> void Page_Load(object Sender, EventArgs e) { if (!IsPostBack) { CreateDataGrid( ); } } void CreateDataGrid( ) { DataSet dsGrid = new DataSet( ); dsGrid = (DataSet) ConfigurationSettings.GetConfig( "system.web/DataSetSectionHandler"); dg.DataSource=dsGrid.Tables[0]; dg.DataBind( ); } </script> The page in Example 20-25 first imports two namespaces necessary for working with the SQL Server database. The interesting work is done in the CreateDataGrid method. There, rather than supply a DSN and SQL query string, a call is made to the GetConfig method of the ConfigurationSettings class, which returns a DataSet object directly. Then the DataSet object is set as the DataSource of the DataGrid control, and the control is data bound. The parameter of the GetConfig method is a string containing the name of the section containing the configuration settings. Notice the syntax with the section name (system.web) separated from the subsection name (DataSetSectionHandler) by a slash. ![]() |
![]() ![]() |