Chapter 3. Configuration

Ask any three traditional ASP developers what they like least about building applications in ASP and you will always get three different answers. Of course, it will always be the same three different answers. One of the developers will claim that debugging is absolutely the worst part of ASP development. A second developer will state emphatically that deployment is by far the worst part of ASP development. And the third developer will claim that no one thing is the worst, but the combination of debugging and deployment is the worst part of ASP development.

When the ASP.NET team set out to design the next version of ASP, many of their top priorities were to remedy the most glaring deficiencies of ASP. The debugging of ASP.NET applications is covered in Chapter 5, but fear not, it does improve. As for deployment improvements, Chapter 1 discussed the solution to one of the most frustrating aspects of deployment with ASP applications: the fact that you had to stop IIS to replace any DLLs (usually COM libraries) that were used by your application. ASP.NET remedies this by introducing the shadow copy mechanism for all referenced assemblies placed in the /bin directory of the application, so neither the ASP.NET worker process nor IIS has to be stopped to replace components associated with an application. This feature is often categorized as part of the xcopy deployment feature of ASP.NET—the fact that for most ASP.NET applications, installing or upgrading an application is as simple as using xcopy (or a similar copy utility) to place new files on the server.

The xcopy deployment of ASP.NET would not be complete, however, if it were not also possible to configure ASP.NET applications by copying a file. For a traditional ASP application, all the configuration settings are stored in the IIS metabase, which is a binary repository managed through a set of COM APIs. Installing an ASP application on a server means that the metabase must be updated to reflect the configuration settings of the application, which means that a person with adequate privileges must run a script on the server. Similarly, updating an ASP application requires running a script on the server to update the metabase. In addition, it is not trivial to discover what the current settings for an application are, because the metabase is stored in the registry in a proprietary binary format, unreadable by any means except the COM APIs.

In keeping with ASP.NET's goal of supporting xcopy deployment, the configuration for an ASP.NET application is specified through an XML file, named web.config, placed at the top of the virtual root for the application. In fact, this file uses the same configuration layout that all .NET applications use, where the configuration file is named after the application, such as myapp.exe.config. The web.config file is just a specially named file whose settings apply to the pages and classes within that directory for an ASP.NET application, since ASP.NET applications are housed in the larger aspnet_wp.exe containing process. Because the web.config XML file is stored as plain text, it is easy to view with any text editor or to manipulate programmatically with any XML API. It is also easy to deploy, simply by copying a new version of the text file to the server. When the next request is serviced, ASP.NET will notice that the timestamp on the web.config file has changed and will immediately apply the new settings to that request and all subsequent requests.

web.config

If you have configured a Web application with IIS before, many of the configuration settings available in web.config will be familiar to you. To begin with, let's see how to use the web.config file to configure something simple, such as changing the session state timeout for an application. At the top of your virtual root directory, you would create a new text file named web.config, the contents of which specify a new value for the timeout attribute of the sessionState element, as shown in Listing 3-1.

Example 3-1. Sample web.config File Changing the Session State Timeout

<configuration>
  <system.web>
    <sessionState mode='Inproc' timeout='10' />
  </system.web>
</configuration>

Note that the format of this file is standard XML, with a single top-level element named configuration. Most of the settings applied to ASP.NET applications are elements within the <system.web> element, so that is the next element specified. The sessionState element has a number of attributes, and even though in this case, we care about only the timeout attribute, we also have to specify the mode attribute because it is required for this element. Once this file is placed at the root of our virtual directory, any subsequent requests will pick up the settings we have specified, which in our case means that the session state timeout would be set to 10 minutes, instead of the default 20 minutes. A complete list of all the top-level elements that apply to ASP.NET applications and their purpose is shown in Table 3-1.

Table 3-1. Top-Level Configuration Elements Available in web.config

Element

Purpose

<authentication>

Specify the client authentication mode to use

<authorization>

Allow or deny users or roles access

<browserCaps>

Specify browser capabilities based on user agent

<clientTarget>

Define client targets

<compilation>

Control page compilation and assembly references

<customErrors>

Control error page display and define custom error pages

<globalization>

Set the request and response encoding

<httpHandlers>

Add or remove HTTP handlers

<httpModules>

Add or remove HTTP modules

<httpRuntime>

Control aspects of HTTP request processing

<identity>

Specify impersonation for this application

<machineKey>

Control the validation and decryption key

<pages>

Set defaults for page attributes globally

<processModel>

Control the behavior of the worker process

<securityPolicy>

Define trust levels with associated policy files

<sessionState>

Control session state

<trace>

Enable application-wide tracing

<trust>

Select which trust level to use

<webServices>

Specify Web service protocols and extensions

<appSettings>

Add application-specific data elements

Configuration Hierarchy

While it is most common to deploy a web.config file at the root of your virtual root directory for an ASP.NET application, there are several additional places where web.config files can be placed. ASP.NET supports the hierarchical application of configuration settings in a top-down fashion. Configuration files can be placed in four locations: machine, site, application, and subdirectory.

At the top of the hierarchy is a single, machine-wide configuration file called machine.config, which contains the default settings for all ASP.NET applications on that machine. This file can be found in your $FRAMEWORKCONFIG directory, where $FRAMEWORK is the path to the .NET installation (typically something like c:winntMicrosoft.NETFrameworkv1.0.3705). The machine.config file is the only required configuration file; all web.config files are optional and are necessary only if you want to change some of the default settings defined in machine.config for your application.

The next configuration file to be consulted is the web.config file placed in the root directory of the Web site. Any configuration settings in this file are applied to all ASP.NET applications running on that site.

After that, the web.config file at the root of the virtual directory of an ASP.NET application is consulted, and any configuration settings are applied to all pages and directories within that application.

Finally, you can also place web.config files within subdirectories under the virtual root. Any configuration settings in a subdirectory configuration file apply to all pages within that subdirectory or any subdirectories below it. It is important to note that the subdirectory structure used to apply configuration settings is the one specified in the URL path, not the physical directory path on disk (although typically they mirror each other).

Figure 3-1 shows the hierarchy of configuration files for an ASP.NET application. This example demonstrates all the configuration files that would affect the ASP.NET application living under the myvdir virtual directory, which is defined within the default site on the machine. Note that when a request maps into a subdirectory of the application, additional web.config files may be applied to the processing for that request.

Hierarchy of Configuration Files

Figure 3-1. Hierarchy of Configuration Files

One of the advantages of this hierarchical composition of configuration settings is the level of granularity it allows. You can, for example, localize the configuration settings for a collection of pages within your application that reside in a particular subdirectory, without having to alter the top-level configuration file for that application. If you ever decide to move that subdirectory of pages to another application, their configuration settings will follow them. The disadvantage of this model is that you can never be sure exactly what configuration settings are being applied to a particular page in your application without inspecting all configuration files that apply. As you begin to work with ASP.NET configuration files, keep this in mind, and be sure to check all four configuration file locations if you see any unexpected behavior.

For example, consider the application of the three configuration files shown in Figure 3-2. The top-level machine.config file contains an httpHandlers element that maps requests for files ending in .ashx to a class called SimpleHandlerFactory. On a virtual directory on the machine (c:inetpubwwwrootfoo) is another configuration file, with no additional settings, so requests made for .ashx files at this location are forwarded to the SimpleHandlerFactory class. However, in a subdirectory (c:inetpubwwwrootfooar) a third configuration file explicitly removes the .ashx handler from the list of handlers. If a request for an .ashx file is made to this subdirectory, it will not be forwarded to the SimpleHandlerFactory class as it would have just a directory above. Although it may be convenient to use elements with this subtractive capability, it is often difficult to predict their behavior at a particular point on the disk without knowing all other configuration files that may affect their behavior.

Hierarchical Configuration Settings Example

Figure 3-2. Hierarchical Configuration Settings Example

Location Element

Depending on your application design, you may find it more convenient to specify configuration settings for subdirectories and files in a single configuration file instead of spreading multiple configuration files throughout your directory structure. Among other things, this approach makes it easier to see exactly what settings apply to what files by looking at a single configuration file. ASP.NET supports multiple configuration settings in a single file using the location element. The location element takes an attribute named path, which can be a path to a file or a directory, and acts as a mini configuration file within the primary configuration file. To see an example, we could write a single top-level web.config file using the location element to perform the equivalent of what the two configuration files shown in Figure 3-2 performed, as shown in Listing 3-2.

Example 3-2. Using the location Element

<configuration>
  <location path="bar">
    <system.web>
      <httpHandlers>
        <remove verb="*" path="*.ashx" />
      </httpHandlers>
    </system.web>
  </location>
</configuration>

This technique is particularly useful when granting and revoking authorization rights to various sections of your application, as we will see in Chapter 11.

Element Placement

Most of the configuration elements available can be defined in any of the four configuration file locations: machine, site, application, and subdirectory. However, a subset of settings have restrictions on where they can be used. The authentication, sessionState, trust, and httpModules[7] elements can be defined only at the machine, site, or application levels, not at the subdirectory level. These elements all affect how the application behaves overall, and it would not make sense to apply them to only a subset of pages in the application. The processModel element is even more restricted and can exist only at the machine level. As we will see, this element controls the machine-wide process management for ASP.NET and thus cannot be applied anywhere but at the machine level.

Impact of Configuration Changes

Any changes made to a web.config file are detected by ASP.NET on the next request, and the application domain for that application is reloaded. Among other considerations, it is important to note that this causes any in-process session state and any application state to be lost. Because of this, changes to a configuration file should be kept to a minimum on a live server, and if in-process session state or application state is being used, be sure the configuration files are updated at a time that will not inconvenience users of the application. Any changes to the processModel element in the machine.config file are an additional special case. These changes will not be applied until the worker process is terminated and restarted, either by performing an IIS reset, by manually killing the aspnet_wp.exe process, or through the worker process bouncing itself for any number of reasons. Terminating the worker process loses session and application state not only for that application but for all ASP.NET applications running on that machine. For this reason, changes to the process model for a machine should happen extremely infrequently on a production machine.

IIS and web.config

In the current release of ASP.NET, IIS is always listening for requests and dispatching them to the ASP.NET worker process if they are ASP.NET requests. This important to realize because the configuration settings in the IIS metabase are applied before the request to the ASP.NET worker process is dispatched.

One important example of this is security. Security is configurable through the metabase for any Web application hosted with IIS. ASP.NET also has a number of configuration settings related to security, but the IIS settings are applied before the ASP.NET settings. For example, if you specify in the IIS metabase that users must be authenticated using Windows authentication, but in your ASP.NET application web.config file you have granted anonymous access, users will always be required to authenticate before they can access pages in your application. For this reason, it is a good practice to leave the IIS metabase settings at their defaults and to specify all application configuration through the ASP.NET web.config file. This strategy also simplifies installation because the only thing you need to set up in the IIS metabase is the virtual directory pointing to the ASP.NET application.

Configuration Data

When Web applications are deployed, there are often constant data values that need to be modified so that the application runs properly on the deployment server. Examples of such values include database connection strings and preferences or settings that influence the appearance or behavior of the application. ASP.NET configuration files provide a specific element for storing generic name/value pairs, called appSettings, which is ideal for storing these types of settings. It supports a subelement called add, which takes a key and value pair of attributes, as shown in Listing 3-3.

Example 3-3. Specifying Application-Specific Configuration Data

<!— File: web.config —>
<configuration>
  <appSettings>
    <add key="DSN"
         value="server=localhost;uid=sa;pwd=;database=pubs"
    />
    <add key="bgColor" value="white" />
  </appSettings>
</configuration>

In this example, we have chosen to store two pieces of application-specific data: a data source connection string and a background color. Placing data like this in a configuration file makes it very easy to customize these values by simply changing them in the configuration file. This is especially appealing for data that may change from one site installation to another. It is also an efficient mechanism for storing and retrieving small amounts of data, because the entire contents of the configuration file are loaded into memory when the application starts, so there is no file access involved with retrieving the values once they have been loaded. Once key/value pairs have been added to the appSettings element, you can extract them from any page or object in your application using the ConfigurationSettings class, as shown in Listing 3-4.

Example 3-4. Retrieving appSettings Configuration Data

<!— File: samplepage.aspx —>
<%@ Page Language='C#' %>
<%@ Import Namespace='System.Configuration' %>

<script runat=server>
protected void Page_Load(object src, EventArgs e)
{
  string dsn = ConfigurationSettings.AppSettings["DSN"];
  // use dsn to connect to a database...

  string bgColor =
           ConfigurationSettings.AppSettings["bgColor"];
  // use retrieved background color...
}
</script>

<!— remainder of page not shown —>

Note in Listing 3-4 that the namespace System.Configuration was imported, because that is where the ConfigurationSettings class resides. This class provides a static indexer called AppSettings that is used to retrieve the values indexed by their key in the appSettings element of a configuration file. The keys used to index the appSettings element are not case sensitive, so be aware that bgColor and BgColor, for example, will map to the same element.

One frequently asked question is, What prevents someone from directly accessing the web.config file in my application, potentially revealing the database login information and other data that should be protected? Fortunately, ASP.NET has a built-in handler called the HttpForbiddenHandler that is designed to restrict access to particular files. Among the files designated to use this handler are any files ending with the extension .config (also protected are .cs, .vb, .asax, .resx, and others). When this handler is invoked by an attempt to access any file with a forbidden extension, it returns an HTTP error code of 403, indicating that the access is forbidden; so by default, configuration settings are inaccessible to external clients.

Process Model

One of the most interesting configuration elements available is the processModel element. It is different from all the other configuration elements in several ways.

  • It can be placed only in the systemwide machine.config file.

  • Changes to this element do not take effect until the worker process is restarted.

  • The configuration settings defined in this element are read in by the unmanaged aspnet_isapi.dll ISAPI extension DLL instead of the managed mechanism used by the other settings.

This element controls various aspects of the ASP.NET worker process (aspnet_wp.exe), including its lifetime, how many instances are created at a time, what security identity it runs under, and how large a thread pool it should use to service requests. Table 3-2 shows the attributes available with this element.

Table 3-2. Attributes of the processModel Element

Attribute

Values

Default

Description

Enable

true | false

true

Whether ASP.NET is hosted in an external worker process (true) or directly in inetinfo.exe (false)

timeout

Infinite | HH:MM:SS

Infinite

Total life of a process—process bounced after timeout

idleTimeout

Infinite | HH:MM:SS

Infinite

Total idle life of a process—process bounced when reached

shutdownTimeout

Infinite | HH:MM:SS

0:00:05

Time given to process to shut down before being killed

requestLimit

Infinite | number

Infinite

Total number of requests to serve before bouncing process

requestQueueLimit

Infinite | number

5000

Number of queued requests allowed before bouncing process

restartQueueLimit

Infinite | number

10

Number of requests kept in queue while process is restarting

memoryLimit

Number

60

Percentage of physical memory process is allowed to use before bouncing process

webGarden

true | false

false

Whether process should be affinitized with a particular CPU (for multi-CPU machines)

cpuMask

Bitmask

0xffffffff

Controls number of CPUs available for ASP.NET worker processes (webGarden must be true)

userName

SYSTEM | MACHINE | username

MACHINE

Windows identity to run the worker process in (MACHINE uses low-privileged ASPNET account)

Password

AutoGenerate | password

AutoGenerate

Password for username

logLevel

All | None | Errors

Errors

Event types logged to event log

clientConnectedCheck

HH:MM:SS

0:00:05

Time a request is left in the queue before a client-connected check is performed

comAuthenticationLevel

Default | None | Connect | Call | Pkt | PktIntegrity | PktPrivacy

Connect

Level of authentication for DCOM security

comImpersonationLevel

Default | Anonymous | Identify | Impersonate | Delegate

Impersonate

Authentication level for COM security

responseRestartDeadlockInterval

Infinite | HH:MM:SS

00:09:00

Time to wait between restarting worker process because of responseRestartDeadlockInterval

responseDeadlockInterval

Infinite | HH:MM:SS

00:03:00

For deadlock detection, timeout for responses when there are queued requests

maxWorkerThreads

Number

25

Maximum number of I/O threads per CPU in the thread pool

maxIoThreads

Number

25

Maximum number of I/O threads per CPU in the thread pool

serverErrorMessageFile

File name

""

Customization for “Server Unavailable” message

Most of the attributes shown in Table 3-2 affect the lifetime of the worker process. At first, it may seem illogical to have such precise control over how long the worker process lives. After all, why not just have it live forever, and be done with it? The sheer number of ways you can request that the worker process self-terminate and restart indicates that the ASP.NET team recognized that sometimes things go wrong—resources leak, deadlocks occur, memory limits are reached, and so on. Some of these things may be beyond your control, and the best choice in those cases is often to simply terminate the worker process and restart it. In many cases, this will have little impact on the servicing of clients because of the disconnected nature of the HTTP protocol. The primary drawbacks of a process bounce are the loss of in-process session state, application state, and entries in the data cache.

This practice of bouncing the process servicing requests is not as uncommon as you may think—many high-volume Web servers today make it a regular practice to bounce the server process periodically. The ASP.NET team recognized this in the beginning and built in a number of safeguards to ensure that the process bounce would occur smoothly and under the right conditions. This was so important in their list of goals that internally they liked to use the slogan “ASP.NET—Designed for Failure,” which of course did not go over very well with the marketing people and was therefore left as an internal slogan only.

As mentioned, the conditions under which you can elect to have the server process bounce are numerous. By default, only two conditions cause the process to bounce: 1) if the total memory utilization of the process exceeds 60% of the physical memory on the machine (specified by the memoryLimit attribute) and 2) if more than 5,000 requests are queued. Both of these conditions are abnormal and should not be encountered under normal operating conditions; but if they do occur, it is likely that something is wrong and that bouncing the process may very well cure it. You can add conditions that will bounce the process, including an explicit timeout (timeout), an idle timeout (accumulated time spent idle—idleTimeout), and an upper bound on the number of requests serviced by a worker process (requestLimit).

You also have some amount of control over CPU utilization through the processModel element. For example, if your server has multiple CPUs, you can enable the webGarden attribute, to request that a dedicated worker process run on each CPU. You can also restrict which CPUs are used to host worker processes if you don't want to use them all, through the cpuMask attribute. For example, if you have a 4 CPU machine, but you want to use only processors 0, 1, and 2 to host ASP.NET worker processes, you specify webGarden as true and cpuMask as 0x00000007, which corresponds to the binary bitmask 0...0111, as shown in Listing 3-5.

Example 3-5. Specifying Multiple Worker Processes on a Multi-CPU machine

<processModel enable="true"
              timeout="Infinite"
              idleTimeout="Infinite"
              shutdownTimeout="0:00:05"
              requestLimit="Infinite"
              requestQueueLimit="5000"
              restartQueueLimit="10"
              memoryLimit="60"
              webGarden="true"
              cpuMask="0x00000007"
              userName="machine"
              password="AutoGenerate"
              logLevel="Errors"
              clientConnectedCheck="0:00:05"
              comAuthenticationLevel="Connect"
              comImpersonationLevel="Impersonate"
              responseRestartDeadlockInterval="00:09:00"
              responseDeadlockInterval="00:03:00"
              maxWorkerThreads="25"
              maxIoThreads="25" />

Be aware that if you enable the webGarden attribute on a multi-CPU machine, session state, application state, and the global data cache are not shared between worker processes. Chapter 10 discusses ways of dealing with state sharing between processes.

The other way you have control over CPU utilization is through the number of worker and I/O threads used within the worker process to service requests. The distinction between the two types of threads is that I/O threads are bound to I/O completion ports and are used to access a particular I/O object (such as a stream or a pipe), and worker threads are traditional unrestricted threads. Currently, ASP.NET processes requests primarily on I/O threads[8] because requests are initiated through an asynchronous write to a named pipe from IIS, the details of which we discuss in Chapter 4. These threads are drawn from the process-wide thread pool maintained for every .NET application. By default, these pools are initialized with 25 threads per CPU on the machine, which is generally a sufficient number of threads to keep the CPU utilization high. If for some reason the requests to your application end up doing a lot of waiting (for external resources, perhaps), limiting the process to 25 threads may be too constraining, in which case you could increase the number to anything less than 100. Be advised, however, that it is generally uncommon for this to be the case, and leaving the thread pools at their default of 25 should almost always be adequate.

The remaining attributes in the processModel element are related to security and are discussed further in Chapter 11.

Accessing Process Information

In addition to controlling various aspects of the process model, you can access information about the worker processes on a machine programmatically. The ProcessModelInfo class provides a pair of static methods to retrieve information about the current worker process and past worker processes that may have terminated recently. Each of these functions returns a reference to a ProcessInfo class populated with information about the worker process, including its age, the maximum amount of memory it has used, its process ID, how many requests it has serviced, when it was started, its status, and why it was shut down (if it was). Listing 3-6 shows the ProcessModelInfo and ProcessInfo classes. Figure 3-3 shows sample output from calling ProcessModelInfo.GetHistory(10).

Example 3-6. ProcessModelInfo and ProcessInfo Classes

public class ProcessModelInfo
{
  public static ProcessInfo GetCurrentProcessInfo();
  public static ProcessInfo[] GetHistory(int num);
}

public class ProcessInfo
{
  public TimeSpan Age {get;}
  public int PeakMemoryUsed {get;}
  public int ProcessID {get;}
  public int RequestCount {get;}
  public ProcessShutdownReason ShutdownReason {get;}
  public DateTime StartTime {get;}
  public ProcessStatus Status {get;}
}
Sample ProcessModelInfo Output

Figure 3-3. Sample ProcessModelInfo Output

IIS 6.0 Process Model Changes

With the release of IIS 6.0 in Windows Server 2003, the process model changes dramatically. To begin with, the processModel element in machine.config is ignored because it is replaced with equivalent settings in the IIS metabase, which is now stored in XML format in the metabase.xml file. ASP.NET is no longer hosted in aspnet_wp.exe but in one or more instances of w3wp.exe. Even more significantly, you are no longer constrained to just one worker process per CPU on a particular machine. Instead, you can configure what are called application pools, which contain collections of virtual directories that all share the same worker process. The properties of each application pool control how that particular worker process behaves, including even more settings than are available in the processModel element. Some of the new process model settings include the ability to set specific times of the day when the process should recycle; separate memory limits for virtual memory and actual used memory; CPU usage monitoring with the ability to recycle the process if utilization is too high; rapid-fail protection by disabling the application pool if it encounters a fixed number of failures within a particular time limit; and start-up and shutdown time limits.

The other significant change is that HTTP requests are now handled in kernel mode through the http.sys service. This service listens for HTTP requests and places them in the appropriate application queue. This means that inetinfo.exe is no longer the front end for HTTP requests, so the advantage of servicing requests in-process is gone. All requests are routed through the kernel-mode HTTP listener and dispatched to some process for servicing. Moving HTTP request queuing into the kernel means that faults in user-mode processes cannot adversely affect the HTTP listener, and even if a crash occurs in the user-mode request processing infrastructure, the kernel service will continue to accept and queue up requests until either the queues completely fill up or the service is shut down.

If you are moving an ASP.NET application to IIS 6.0, but you still want to use the process model and configuration settings of IIS 5.0, you can set a backward-compatibility flag in IIS 6.0 that causes it to run in IIS 5.0 isolation mode. This must be applied at the machine level, because it affects how all requests are processed on a given machine.

Additional Settings

The best way to familiarize yourself with the various settings available for ASP.NET applications is to open the machine.config file on your system and peruse the elements. Each element is carefully documented with available child elements and attributes, and many examples are included. It is also in this file that you can find out what the defaults are for all of the ASP.NET applications on your machine. We discuss some of the additional elements in later chapters, where they are more directly relevant to the topic at hand.

As you begin to develop ASP.NET applications, you should be aware of two additional configuration elements: the assemblies element and the pages element. If you find you are using the @Assembly directive to add a reference to a GAC-deployed assembly to several pages in an application, you can instead reference that assembly once globally in the application's web.config file using the assemblies element. For example, suppose you have built a utilities assembly called Util that contains a number of utility classes used throughout the applications on your server. If you decide to deploy this assembly in the GAC, you can add an implicit reference to the assembly to all pages in your application by adding it to the assemblies element in your root web.config file, as shown in Listing 3-7.

Example 3-7. Adding an Application-wide Reference to a GAC-Deployed Assembly

<configuration>
  <!— ... —>
  <system.web>
  <compilation>
    <assemblies>
      <add assembly="Util, Version=1.0.0.0, Culture=neutral,
Adding an Application-wide Reference to a GAC-Deployed Assembly PublicKeyToken=a77a5c561934e089" />
    </assemblies>
  </compilation>
  </system.web>
</configuration>

Similarly, if you find that you are repeating the same @Page directives for many of the pages in your application, you can instead use the pages element in your application-wide web.config file to change the default for many of the @Page directive attributes. For example, suppose you wanted to disable view state for all pages in your application by default. Listing 3-8 shows the configuration file necessary to do this.

Example 3-8. Using the pages Element

<configuration>
  <!— ... —>
  <system.web>
  <pages enableViewState='false' />
  </system.web>
</configuration>

Reading Configuration Information

We have already seen how to retrieve values stored in the AppSettings element by calling ConfigurationSettings.AppSettings["xxx"], where xxx is the key we used to index the value. This static indexer is actually a convenience wrapper around a more general way of retrieving any configuration setting element through the ConfigurationSettings.GetConfig() method, as shown in Listing 3-9.

Example 3-9. Reading Configuration Settings

object settings =
            ConfigurationSettings.GetConfig("appSettings");
NameValueCollection nvc = settings as NameValueCollection;
if (nvc != null)
{
  string val = (string)nvc["xxx"];
}

Any configuration element (except the processModel element) can be retrieved using this technique. Internally, this maps into a request for the cached settings of the configuration file for that element, and if they have not yet been cached, a request to read them. At the lowest level of the configuration hierarchy, the physical XML files on disk are read by a class called ConfigurationRecord, using the XmlTextReader class to efficiently pull in the configuration data. Configuration information is parsed and stored on demand, so if a particular configuration element is never requested, it will never be read and loaded into memory. The job of parsing individual sections of the configuration file falls to configuration section handlers.

Conceptually, all ASP.NET configuration files are divided into two sections: the configuration section handlers and the configuration data. Until now, we have been adding information to the configuration data section only and have not even included the configuration section handler portion in our web.config files. If you look at the machine.config file on your system, however, you will see that the top of the file includes a number of configuration section handlers.

The job of a configuration section handler is to parse a portion of the configuration file, which makes this configuration file format extremely extensible. The classes responsible for reading in portions of the file are not established until the file is actually read, at which point instances of each parser object are created and passed the portion of the file they are responsible for. Figure 3-4 shows this relationship in a portion of the systemwide machine.config file. Note that the compilation section of the configuration file is parsed by a class called CompilationConfigurationHandler, specified under the configSections element. It also falls under a sectionGroup element, which further scopes the name of the section it will parse—in this case, to be under an element named system.web. The other configuration section handler shown in this sample is the one for appSettings. Notice that this element is parsed by a class called NameValueFileSectionHandler and is a top-level element in the configuration file.

Configuration Section Handlers in machine.config

Figure 3-4. Configuration Section Handlers in machine.config

Each class added in the configSections element must implement the IConfigurationSectionHandler interface, shown in Listing 3-10, which has a single method called Create. The Create method is called by the top-level configuration file parser (the ConfigurationRecord class) when a tag designated for that handler is read in the configuration file. When the Create method is called for a handler, it is passed the parent configuration section (if there is one), the current HttpConfigurationContext object (through the input parameter), and most importantly, a reference to the XmlNode to be parsed by the handler. In most cases, the handler simply walks through the child nodes and attributes of the XmlNode passed in. The return value from this function is an object containing whatever state the handler wishes to retain in the running program, and it is cached and available anywhere in the application through the ConfigurationSettings.GetConfig() method.

Example 3-10. IConfigurationSectionHandler Interface

public interface IConfigurationSectionHandler
{
  object Create(object parent, object input, XmlNode node);
}

Most of the ASP.NET configuration section handlers define a corresponding state retainer class that acts as a repository for the configuration settings associated with that handler. For example, a CompilerConfiguration class stores the contents of the compilation element, a PagesConfiguration class stores the contents of the pages element, and so on. Thus, for any active ASP.NET application, there are a number of in-memory instances of configuration classes populated with configuration state. These instances are stored in a hash table that is global to the application, and all calls to the ConfigurationSettings.GetConfig() method are simply accesses into this global hash table, as shown in Figure 3-5.

In-memory Configuration Settings Layout

Figure 3-5. In-memory Configuration Settings Layout

Be aware that most of the configuration classes used by ASP.NET are internal classes and are thus not accessible. ASP.NET uses the configuration classes to set the defaults and other values in the classes it creates.

Building a Custom Configuration Section Handler

Although most commonly used application-specific settings can be stored in the appSettings element, you may sometimes want to augment the set of available configuration elements with your own custom one. Even if you never need to do this, it is still instructive to walk through an implementation of a custom configuration section handler to better understand how the web.config file is parsed.

To see an example of building a custom configuration section handler, suppose we want our applications to be able to specify custom elements using an element called acme, contained in a new section called acmeGroup, as shown in Listing 3-11. The elements we are interested in are font, backgroundColor, underlineLinks, horizontalWidth, and verticalWidth, presumably to customize the way our application appears on a particular installation site.

Example 3-11. Sample Custom Configuration Element

<!— File: web.config —>
<configuration>
  <acmeGroup>
    <acme>
      <font>Courier New</font>
      <backgroundColor>Green</backgroundColor>
      <underlineLinks>true</underlineLinks>
      <horizontalWidth>600</horizontalWidth>
      <verticalWidth>800</verticalWidth>
     </acme>
  </acmeGroup>
</configuration>

To begin with, we must come up with some mechanism for saving the state associated with this configuration. There are many ways of doing this, but to keep things simple, let's take the approach of defining a class with public data members corresponding to the configuration elements we have defined. The AcmeSettings class shown in Listing 3-12 should be suitable.

Example 3-12. Sample Custom Configuration Settings State Class

// File: AcmeSettings.cs
namespace EssentialAspDotNet.Config
{
  public class AcmeSettings
  {
    public string Font;
    public string BackgroundColor;
    public bool   UnderlineLinks;
    public int    HorizontalWidth;
    public int    VerticalWidth;
  }
}

Next, we need to build a class that implements IConfigurationSectionHandler to parse our section of the configuration file and store the contents into our new AcmeSettings class, shown in Listing 3-13.

Example 3-13. Sample Custom Configuration Section Handler

// File: AcmeConfigHandler.cs
namespace EssentialAspDotNet.Config
{
  public class AcmeConfigHandler :
                               IConfigurationSectionHandler
  {
    public object Create(object parent, object input,
                         XmlNode node)
    {
      AcmeSettings aset = new AcmeSettings();

      foreach (XmlNode n in node.ChildNodes)
      {
        switch (n.Name)
        {
          case ("font"):
            aset.Font = n.InnerText;
            break;
          case ("backgroundColor"):
            aset.BackgroundColor = n.InnerText;
            break;
          case ("underlineLinks"):
            aset.UnderlineLinks = bool.Parse(n.InnerText);
            break;
          case ("horizontalWidth"):
            aset.HorizontalWidth = int.Parse(n.InnerText);
            break;
          case ("verticalWidth"):
            aset.VerticalWidth = int.Parse(n.InnerText);
            break;
        }
      }
      return aset;
    }
  }
}

The final step is to tell the configuration file to use this class to parse the acme element in our configuration files. As shown in Listing 3-14, we do this by adding a section element to the configSections portion of our configuration file—this could be in the systemwide machine.config file, the sitewide web.config, or the application-wide web.config, and it will apply to all configuration files parsed after that one is read.

Example 3-14. Installing a Custom Configuration Section Handler

<!— File: web.config —>
<configuration>
  <configSections>
    <sectionGroup name="acmeGroup">
       <section name="acme"
         type="EssentialAspDotNet.Config.AcmeConfigHandler, AcmeConfigHandler"
       />
    </sectionGroup>
  </configSections>
  <!— ... —>
</configuration>

Any page or piece of code in our application could now access this configuration information using the ConfigurationSettings.GetConfig() method, passing in the section group and section name we created, and casting the result to our AcmeSettings class, as shown in Listing 3-15.

Example 3-15. Accessing Custom Configuration Information

// File: TestAcmeSettings.aspx
protected void Page_Load(object src, EventArgs e)
{
  AcmeSettings set;
  set = ConfigurationSettings.GetConfig("acmeGroup/acme")
        as AcmeSettings;

  // use set here (like set.Font, set.BackgroundColor,
  // etc.)
}

Using the NameValueFileSectionHandler

If you want a custom configuration section, but you don't want to go to the trouble of writing your own class that implements the IConfigurationSectionHandler interface, you can create a new section that reuses the same class as the appSettings element. You are limited to using the add element with key/value pairs for adding new configuration elements, but if this is tolerable, adding a new configuration section becomes even easier. For example, Listing 3-16 shows how to add a new section called myGroup to a configuration file and populate it with values similar to our custom acmeGroup shown earlier. Notice that the type specified in the section element refers to a class found in the system assembly called NameValueFileSectionHandler. Instances of this class store the key/ value pairs in a NameValueCollection for later access. Listing 3-17 shows how you would access this collection from within an .aspx page in the application.

Example 3-16. Adding a Custom Configuration Section with a Prebuilt Handler

<!— File: web.config —>
<configuration>
  <configSections>
     <section name="myGroup"
    type="System.Configuration.NameValueFileSectionHandler, System, Version=1.0.3300.0,
Adding a Custom Configuration Section with a Prebuilt Handler Culture=neutral,
PublicKeyToken=b77a5c561934e089"/>

  </configSections>

  <myGroup>
    <add key="font" value="Courier New"/>
    <add key="backgroundColor" value="Green"/>
    <add key="underlineLinks" value="true"/>
    <add key="horizontalWidth" value="600"/>
    <add key="verticalWidth" value="800"/>
  </myGroup>
  <!— ... —>
</configuration>

Example 3-17. Accessing Custom Configuration Information with NameValueCollection

// File: TestAcmeSettings.aspx
protected void Page_Load(object src, EventArgs e)
{
  NameValueCollection set;
  set = ConfigurationSettings.GetConfig("myGroup")
        as NameValueCollection;

  // use set here (like set["Font"],
  // set["BackgroundColor"], etc.)
}

SUMMARY

ASP.NET applications are configured using a set of XML configuration files named web.config. These configuration files replace the role of the metabase in IIS and enable configuration changes by simply copying new files onto the server. Configuration files can be placed in several places on a Web server. There is always a top-level machine.config file that contains the default settings for all ASP.NET applications deployed on that machine. You can place a web.config file in the root directory of a site that applies to all applications deployed on that site. You can also place web.config files at the top level of a virtual directory or in any subdirectory of an application, and the settings are applied hierarchically, with local configuration files overriding higher-level ones.

In addition to the typical Web application settings, several new elements are available. The appSettings element is useful for storing generic name/value pairs of data for retrieval during application execution. The processModel element gives you very precise control over how the lifetime of the ASP.NET worker process is managed. Finally, you can also create your own configuration sections by authoring a class that implements IConfigurationSectionHandler or by using the provided NameValueFileSectionHandler class.



[7] In the current release, httpModules are allowed in subdirectory configuration files, but they do not take effect. In a future release, they will not even be allowed.

[8] This changes in Windows Server 2003 with IIS 6.0. Because ASP.NET is directly integrated into IIS 6.0, there is no longer any need to dispatch requests to ASP.NET over named pipes, so requests are processed on worker threads instead of I/O threads.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.178.181