Team LiB
Previous Section Next Section

Configuration API

The new .NET Framework offers a big API to allow typed access to the configuration settings. More than in the earlier version of the .NET Framework, you can not only read, but also update and write the settings. This way, you can change the configuration for your web site very conveniently without needing to work manually with the web.config file. Even changes of the configuration in subdirectories are possible this way. Both of the tools that I've presented to you in this chapter are already intensively using this new API.

Accessing Configuration

The starting point for reading settings is the Configuration class taken from the System.Configuration namespace. You can obtain an instance of the class for the desired configuration area by using one of its several static methods.

For example, to detect the root configuration for the current application, you can use the GetConfigurationForUrl() method and pass the virtual path of the application:

string appPath = this.Request.ApplicationPath;
Configuration config = Configuration.GetConfigurationForUrl(appPath);

By means of a couple of properties from the returned class instance you can get some information on the configuration file. With the HasFile property, for example, you can detect whether the file already exists (if this isn't the case, the inherited settings will be used) and identify its physical filename with the help of FilePath.

Naturally, the current possibilities for accessing the settings will be supported further, but with the API a new way is offered to you. Depending on the application area, you must make up your mind which way to go.

Accessing Application Settings and Connection Strings

You know application settings from the first version of ASP.NET. They are nothing but a simple NameValueCollection by which you can deposit individual configuration settings in the web.config file and provide it with a unique key. This looks like the following:

<?xml version="1.0"?>
<configuration>
    <appSettings>
        <add key="test1" value="This is my first v2 app settings value"/>
        <add key="test2" value="And this is my second one"/>
    </appSettings>
</configuration>

Access to the settings through the API is shown in the following example. AppSettingsSection class used in the following code snippet comes from the System.Web.Management namespace, which is used for the configuration of web sites in addition to the System.Web.Configuration.

string appPath = this.Request.ApplicationPath;
Configuration config = Configuration.GetConfigurationForUrl(appPath);
AppSettingsSection appSettings = config.AppSettings;

foreach (string key in appSettings.Settings)
{
    this.Response.Write(string.Format("<p><b>{0}</b> = {1}</p>",
        key, appSettings.Settings[key]));
}

You can also use the existing query options through the ConfigurationSettings class without directly using the API, as you can see in Figure 13-4. In this case, however, you won't have the option to change data as described in the "Updating Configuration" section.

Click To expand
Figure 13-4: You can access the application settings either using the old method or with the new Configuration API.
foreach (string key in ConfigurationSettings.AppSettings)
{
    this.Response.Write(string.Format("<p><b>{0}</b> = {1}</p>",
        key, ConfigurationSettings.AppSettings[key]));
}

In the past, application settings have frequently been used to store connection strings. This way, they are saved at a central position outside of the source code and can be switched easily between the development and the live platform. The new version, however, has a separate area that is exclusively designated for the storage of connection strings.

This new area is used by different providers employed for site counters, Membership Management, and Role Management, as well as for personalization. For example, if you want to replace the SQL Server that is being used by the providers, you just have to modify one single connection string in the machine.config file or overwrite it in the local web.config file.

And here's how the new section with its two default values in the machine.config looks like:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <connectionStrings>
          <add name="LocalSqlServer"
              connectionString="data source=127.0.0.1;Integrated Security=SSPI" />
          <add name="AccessFileName" connectionString="~\DATA\ASPNetDB.mdb" />
    </connectionStrings>
</configuration>

The usage of connection strings with the new API looks similar to the usage of application settings. The Configuration class doesn't offer typed access, though. Therefore, they have to be queried through the collection that is returned by the Sections property.

string appPath = this.Request.ApplicationPath;
Configuration config = Configuration.GetConfigurationForUrl(appPath);
ConnectionStringsSection connStrings =
    (ConnectionStringsSection) config.Sections["connectionStrings"];

foreach(ConnectionStringSettings connString in connStrings.ConnectionStrings)
{
    this.Response.Write(string.Format("<p><b>{0}</b> = {1}</p>",
        connString.Name, connString.ConnectionString));
}

In case of a read-only access, you can use the following abbreviation:

foreach(string key in ConfigurationSettings.ConnectionStrings)
{
    this.Response.Write(string.Format("<p><b>{0}</b> = {1}</p>",
        key, ConfigurationSettings.ConnectionStrings[key]));
}

You must use the following line if you want to query a named and specific connection string within your web site or even from a custom provider (see Figure 13-5):

ConfigurationSettings.ConnectionStrings["key"]

Click To expand
Figure 13-5: Accessing the new connection string section is very easy, too.
Note 

At present, the SqlDataSource and AccessDataSource classes don't incorporate the connection strings that have been deposited in the configuration. It is foreseen that such support will be given with the Beta version so that you can centrally exchange the applied connection strings.

Accessing Other (Well-Known) Web Configurations

It's really worth it to take a good look at the Web property of the Configuration class. It returns a big object model that covers the complete standard configuration of an ASP.NET web site. The property returns a class of the type WebConfigurationSectionGroup whose properties reflect the already-known configuration sections: AnonymousIdentification, Authentication, Authorization, Compilation, and many, many more. Each property again delivers an object that allows typed access on each and every attribute and subelement of the section the object represents.

The page implemented in Listing 13-1 and shown in Figure 13-6 uses the new API with the intention of displaying all the active authorization rules. It becomes quite clear how detailed the object that has been implemented is because there is a class for the section, one for the rules, and a collection to hold the rules. The rule type is defined through an enumeration.

Click To expand
Figure 13-6: Everyone is welcome to visit my site.
Listing 13-1: Acquiring and Displaying All Authorization Rules in the Browser Window
Start example
<%@ page language="C#" %>
<%@ import namespace="System.Web.Management" %>

<script runat="server">

void Page_Load(object sender, System.EventArgs e)
{
    string appPath = this.Request.ApplicationPath;
    Configuration config = Configuration.GetConfigurationForUrl(appPath);
    AuthorizationSection authorization = config.Web.Authorization;

    this.DL_Rules.DataSource = authorization.Rules;
    this.DL_Rules.DataBind();
}

</script>

<html>
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form runat="server">
        <asp:datalist id="DL_Rules" runat="server">
            <itemtemplate>
                Action: <asp:literal id="Literal1" runat="server"
                    text='<%# ((AuthorizationRule) Container.DataItem).Action %>'/>
                <br />
                Users:
                <asp:repeater runat="server"
                    DataSource='<%# ((AuthorizationRule)
                       Container.DataItem).Users %>'>
                    <itemtemplate><%# Container.DataItem %></itemtemplate>
                    <separatortemplate>-</separatortemplate>
                </asp:repeater>
                <br />
                Roles:
                <asp:repeater runat="server"
                    datasource='<%# ((AuthorizationRule)
                        Container.DataItem).Roles %>'>
                    <itemtemplate><%# Container.DataItem %></itemtemplate>
                    <separatortemplate>-</separatortemplate>
                </asp:repeater>
            </itemtemplate>
        </asp:datalist>
    </form>
</body>
</html>
End example

Access to many other configuration values is done in a similar way. No wonder the System.Management namespace consists of more than 100 different classes and enumerations.

Accessing Other (Not Well-Known) Web Configurations

You can access not only the known standard elements through the new API, but also any others as well. Access takes place through the two Sections and SectionsGroups properties of the Configuration class, as demonstrated in Listing 13-2. A SectionGroup object again contains Sections as well as additional subgroups. The objects will be used in this example to list all the groups including their first-level subsections in a ListBox control (see Figure 13-7).

Click To expand
Figure 13-7: The nested collections allow you to dive deep into the configuration.
Listing 13-2: Giving Access to Any Configuration Section Through the Configuration Class
Start example
<%@ page language="C#" %>

<script runat="server">

void Page_Load(object sender, System.EventArgs e)
{
    string appPath = this.Request.ApplicationPath;
    Configuration config = Configuration.GetConfigurationForUrl(appPath);

    for (int i = 0; i < config.SectionGroups.Count; i++)
    {
        ConfigurationSectionGroup group = config.SectionGroups[i];
        this.LB_Sections.Items.Add(group.Name);

        for (int j = 0; j < group.Sections.Count; j++)
        {
            ConfigurationSection section = group.Sections[j];
            this.LB_Sections.Items.Add(string.Format("...{0}", section.Name));
        }
    }
}

</script>

<html>
<head id="Head1" runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="Form1" runat="server">
        <asp:listbox id="LB_Sections" runat="server" width="300px" height="300px">
        </asp:listbox>
    </form>
</body>
</html>

End example

All sections are represented by classes derived from the abstract base ConfigurationSection, as shown in Listing 13-3. You can access the contained data by the offered properties in each case, or alternatively query the underlying configuration directly as XML (see Figure 13-8).

Click To expand
Figure 13-8: This is really a raw configuration XML.
Listing 13-3: Accessing the Configuration Data As Raw XML Data
Start example
<%@ page language="C#" %>

<script runat="server">

void Page_Load(object sender, System.EventArgs e)
{
    string appPath = this.Request.ApplicationPath;
    Configuration config = Configuration.GetConfigurationForUrl(appPath);
    ConfigurationSection section = config.Sections["appSettings"];
    this.TB_XML.Text = section.GetRawXml().OuterXml;
}

</script>

<html>
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form runat="server">
        <asp:textbox id="TB_XML" runat="server"
            textmode="MultiLine" width="300px" height="100px">
        </asp:textbox>
    </form>
</body>
</html>
End example

Updating Configuration

The real innovation of the API is the possibility to update entries and write the configurations back to the corresponding web.config file. The properties of the various section objects are therefore implemented as get and set. The various collections as well allow you quite often to delete or to add entries. After you have made the desired changes directly in the object model, you can write them back in one step using the Configuration.Update() method.

Caution 

Please be aware that changes take effect directly with the next request of the page. In case of an incorrect application, it might happen that the web site won't work any more. For example, I deactivated the anonymous authentication in a test project, although the anonymous user was activated for some properties of the profile. The result was that the page couldn't be requested any more and I had to undo the change manually.

Updating Application Settings and Connection Strings

Application settings is one of the collections you can modify and enhance very easily. The following example shows the addition of a new entry to the web.config configuration file:

string appPath = this.Request.ApplicationPath;
Configuration config = Configuration.GetConfigurationForUrl(appPath);
AppSettingsSection appSettings = config.AppSettings;

appSettings.Settings.Add("test3", "This one was created using the new API");
config.Update();

foreach (string key in appSettings.Settings)
{
    this.Response.Write(string.Format("<p><b>{0}</b> = {1}</p>",
        key, appSettings.Settings[key]));
}

The changes will be saved at once and are available with the next request of the page—without a complete and annoying restart of the application, of course! After running the example, the configurations file looks as follows:

<?xml version="1.0"?>
<configuration>

    <appSettings>
        <add key="test1" value="This is my first v2 app settings value" />
        <add key="test2" value="And this is my second one" />
        <add key="test3" value="This one was created using the new API" />
    </appSettings>

    <system.web>
        ...
    </system.web>

</configuration>

The procedure is quite the same with connection strings. Here you can update the delivered collection as well. Additionally, the inherited entries can be removed as you can see in the second example in this code snippet:

string appPath = this.Request.ApplicationPath;
Configuration config = Configuration.GetConfigurationForUrl(appPath);
ConnectionStringsSection connStrings =
    (ConnectionStringsSection) config.Sections["connectionStrings"];

connStrings.ConnectionStrings.Remove("LocalSqlServer");

ConnectionStringSettings connString = new ConnectionStringSettings();
connString.Name = "test";
connString.ConnectionString = "data source=127.0.0.1;Integrated Security=SSPI";
connStrings.ConnectionStrings.Add(connString);

config.Update();

The current configuration file now contains a <remove> tag:

<?xml version="1.0"?>
<configuration>

    <connectionStrings>
        <remove name="LocalSqlServer" />
        <add name="test"
            connectionString="data source=127.0.0.1;Integrated Security=SSPI" />
    </connectionStrings>

    <system.web>
        ...
    </system.web>

</configuration>

Updating Other Web Configurations

Analogous to the preceding examples, you can make changes in the configuration settings for web sites through the shown object model. The following listing shows, for example, the creation of a new authorization rule.

string appPath = this.Request.ApplicationPath;
Configuration config = Configuration.GetConfigurationForUrl(appPath);
AuthorizationSection authorization = config.Web.Authorization;

AuthorizationRule rule = new AuthorizationRule();
rule.Action = AuthorizationRuleAction.Allow;
rule.Users.Add("TestUser");
rule.Roles.Add("TestRole");
authorization.Rules.Add(rule);

config.Update();

Or do you want to switch the debug mode of the example on or off? No problem at all! Simply change the settings and call Update()!

string appPath = this.Request.ApplicationPath;
Configuration config = Configuration.GetConfigurationForUrl(appPath);

CompilationSection compilation = config.Web.Compilation;
compilation.Debug = (!compilation.Debug);

config.Update();

Team LiB
Previous Section Next Section