ProblemYou want to create and add your own configuration elements to web.config. No predefined element will do, nor will use of the <appSettings> key/value entries of web.config, as described in Recipe 12.2. SolutionDetermine what configuration information you want to store in web.config, create your own custom section handler for parsing the element, add the definition of the section handler to web.config, add the new configuration element to web.config, and use the configuration information in your application.
The code we've written to illustrate this solution appears in Examples 12-9, 12-10, 12-11, 12-12, 12-13 through 12-14. Examples 12-9 (VB) and 12-10 (C#) show the class for a custom section handler. The changes we've made to web.config to have it use the custom section handler are shown in Example 12-11. Example 12-12 shows the .aspx file for a sample web form that displays some custom configuration settings. Examples 12-13 and 12-14 show the code-behind class that accesses the custom configuration information. The output of the sample web form showing the data read using the custom configuration handler is shown in Figure 12-3. Figure 12-3. Configuration information read by configuration handlerDiscussionSometimes the predefined configuration elements provided by ASP.NET, including the key/value collection available through the <appSettings> element of web.config (described in Recipe 12.2), are not enough. When this is the case, being able to store any configuration information required by your application in its own custom section in web.config can be a useful alternative. What's more, having a custom section in web.config can be a real boon to program clarity and ease of implementation. As a prelude to discussing the custom section for web.config, it's worthwhile reviewing the basic structure of a machine.config file, as shown next, because it is the machine.config file whose structure we will mimic. The root node of the XML document is <configuration>. The <configSections> child element is next. It defines the configuration section elements that will follow and the configuration handlers (classes) that will handle the configuration information contained in the elements. When <configSections> is present, it must be the first child element of <configuration>. <?xml version="1.0" encoding="UTF-8"?> <configuration> <configSections> … </configSections> <!-- sections defined in configSections --> </configuration> Even if you are used to looking at a web.config file, you have probably never seen the <configSections> element. This is because all of the standard configuration handlers are defined in machine.config. (ASP.NET reads the machine.config file and then the web.config file(s) for your application. All of the data from the machine.config file is used unless a section in web.config overrides the equivalent section in machine.config.) Because <configSections> is only used to define configuration handlers, it never appears in a web.config file unless your application uses custom configuration handlers like the one described in this example. In this example, we use a <siteProperties> custom element to hold the information we want to store in web.config and that will be used by our custom configuration handler. We have chosen the path of using a custom element (and a custom configuration handler) because of the flexibility it affords us and the clarity with which we can express the information to be stored. In creating your own custom element, you first need to determine the configuration information you want to store in web.config and then define the format you want it stored in. The only limitation is that the data element must be a well-formed XML node. You can use attributes, child elements, or any combination of the two to hold your custom configuration information. In our example, we use attributes only. The well-formed XML that defines the section we want to add to our application web.config is shown here: <siteProperties applicationName="ASP.NET Cookbook" databaseServer="192.168.0.1" databaseName="ASPNetCookbook_DB" databaseUserName="aspnetcookbook" databaseUserPassword="efficient" emailServer="mail@mailservices.com" />
After defining your configuration element and specifying the values of the information it will store, create a custom configuration handler in a separate project. This way, the created assembly can be reused easily in multiple applications. For our example, the project was named VBCustomConfigHandlers (CSCustomConfigHandlers for C#), which by default will generate an assembly with the same name. In your configuration handler project, add a new class named to reflect the configuration element the handler supports. In our example, we've named the class SiteConfigHandlerVB (SiteConfigHandlerCS for C#) because the section added to web.config contains site configuration information. To act as a custom configuration handler, the class must implement the IConfigurationSectionHandler interface: Namespace VBCustomConfigHandlers Public Class SiteConfigHandlerVB Implements IConfigurationSectionHandler … End Class SiteConfigHandlerVB End Namespace 'VBCustomConfigHandlers namespace CSCustomConfigHandlers { public class SiteConfigHandlerCS : IConfigurationSectionHandler { … } // SiteConfigHandlerCS } // CSCustomConfigHandlers The IConfigurationSectionHandler interface requires that you implement a single method, Create, which requires three parameters: parent, configContext, and section. The parent parameter provides a reference to the corresponding parent configuration section, and the configContext parameter provides a reference to the current ASP.NET context. Neither is used in this example. The section parameter provides a reference to the XML node (section) of the web.config file that is to be processed. In our example, section will be set to reference the <siteProperties> section added to web.config. Namespace VBCustomConfigHandlers Public Class VBSiteConfigHandler Implements IConfigurationSectionHandler Public Function Create(ByVal parent As Object, _ ByVal configContext As Object, _ ByVal section As XmlNode) As Object _ Implements IConfigurationSectionHandler.Create … End Function 'Create End Class 'VBSiteConfigHandler End Namespace 'VBCustomConfigHandlers namespace CSCustomConfigHandlers { public class CSSiteConfigHandler : IConfigurationSectionHandler { public Object Create(Object parent, Object configContext, XmlNode section) { … } // Create } // CSSiteConfigHandler } // CSCustomConfigHandlers The Create method returns an object that contains the configuration information from the passed section. This can be anything you want it to be. The most flexible approach is to define a class that will contain the data and provide easy access by your application. In our example, the returned class is defined in the same file as the SiteConfigHandlerVB (SiteConfigHandlerCS for C#) class. Namespace VBCustomConfigHandlers Public Class VBSiteConfigHandler Implements IConfigurationSectionHandler Public Function Create(ByVal parent As Object, _ ByVal configContext As Object, _ ByVal section As XmlNode) As Object _ Implements IConfigurationSectionHandler.Create … End Function 'Create End Class 'VBSiteConfigHandler 'The following class provides the container returned by 'the SiteConfigHandlerVB Public Class SiteConfigurationVB … End Class 'SiteConfigurationVB End Namespace 'VBCustomConfigHandlers namespace CSCustomConfigHandlers { public class CSSiteConfigHandler : IConfigurationSectionHandler { public Object Create(Object parent, Object configContext, XmlNode section) { … } // Create } // CSSiteConfigHandler // The following class provides the container returned by // the SiteConfigHandlerCS public class SiteConfigurationCS { … } // SiteConfigurationCS } // CSCustomConfigHandlers The SiteConfigurationVB (SiteConfigurationCS for C#) class needs a constructor that has parameters for each of the configuration items and a read-only property for each of the configuration items. The code for our example is shown in Examples 12-9 (VB) and 12-10 (C#). After the class that will be used for the return is defined, the Create method should extract the configuration information from the passed XML section, create a new instance of the SiteConfigurationVB (SiteConfigurationCS for C#) object, and return a reference to the new instance. For our example, attributes were used for the configuration information in the section. This allows us to get a reference to the attributes collection of the section and to extract the individual values by using the GetNamedItem method of the attributes collection. Public Function Create(ByVal parent As Object, _ ByVal configContext As Object, _ ByVal section As XmlNode) As Object _ Implements IConfigurationSectionHandler.Create Dim siteConfig As SiteConfigurationVB Dim attributes As XmlAttributeCollection attributes = section.Attributes With attributes siteConfig = _ New SiteConfigurationVB(.GetNamedItem("applicationName").Value, _ .GetNamedItem("databaseServer").Value, _ .GetNamedItem("databaseName").Value, _ .GetNamedItem("databaseUserName").Value, _ .GetNamedItem("databaseUserPassword").Value, _ .GetNamedItem("emailServer").Value) End With 'attributes Return (siteConfig) End Function 'Create public Object Create(Object parent, Object configContext, XmlNode section) { SiteConfigurationCS siteConfig = null; XmlAttributeCollection attributes = null; attributes = section.Attributes; siteConfig = new SiteConfigurationCS( attributes.GetNamedItem("applicationName").Value, attributes.GetNamedItem("databaseServer").Value, attributes.GetNamedItem("databaseName").Value, attributes.GetNamedItem("databaseUserName").Value, attributes.GetNamedItem("databaseUserPassword").Value, attributes.GetNamedItem("emailServer").Value); return (siteConfig); } // Create With the customconfiguration handler, you need to add the handler information to web.config to tell ASP.NET how to handle the <siteProperties> section you've added. Add a <configSections> element at the top of your web.config that contains a single section element. The name attribute defines the name of the custom section containing your configuration information. The type attribute defines the class and assembly name in the form type="class, assembly" that will process your custom configuration section. The class name must be a fully qualified class name. The assembly name must be the name of the assembly (dll) created in your configuration handler project, described earlier. <configSections> <section name="siteProperties" type="ASPNetCookbook.VBCustomConfigHandlers.SiteConfigHandlerVB, VbCustomConfigHandlers" /> </configSections>
The last thing you need to do before you can use the custom configuration in your application is to add a reference to the VBCustomConfigHandlers (CSCustomConfigHandlers for C#) assembly in your application. Accessing the custom configuration information in your application requires using ConfigurationManager.GetConfig and passing it the name of your custom section. This method returns an object that must be cast to the object type you returned in your custom configuration handler class. After the reference is obtained, all of the site information is available as properties of the object. Dim siteConfig As SiteConfigurationVB siteConfig = CType(ConfigurationManager.GetSection("siteProperties"), _ SiteConfigurationVB) labApplicationName.Text = siteConfig.applicationName labDBServer.Text = siteConfig.databaseServer labDBName.Text = siteConfig.databaseName labDBUserName.Text = siteConfig.databaseUserName labDBUserPassword.Text = siteConfig.databaseUserPassword labEmailServer.Text = siteConfig.emailServer SiteConfigurationCS siteConfig = null; siteConfig = (SiteConfigurationCS) (ConfigurationManager.GetSection("siteProperties")); labApplicationName.Text = siteConfig.applicationName; labDBServer.Text = siteConfig.databaseServer; labDBName.Text = siteConfig.databaseName; labDBUserName.Text = siteConfig.databaseUserName; labDBUserPassword.Text = siteConfig.databaseUserPassword; labEmailServer.Text = siteConfig.emailServer; See AlsoRecipe 12.2 Example 12-9. Custom section handler class (.vb)
Example 12-10. Custom section handler class (.cs)
Example 12-11. Changes to web.config to use the custom section handler
Example 12-12. Sample web form using custom configuration data (.aspx)
Example 12-13. Sample web form using custom configuration data code-behind (.vb)
Example 12-14. Sample web form using custom configuration data code-behind (.cs)
|