|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionOne of the wonderful features of .NET has been its XML configuration features. In the days of .NET 1.x, common application settings, database connection strings, ASP.NET web server configuration and basic custom configuration data could be stored in a *.config file. Custom configuration sections could use a few basic but custom structures, allowing a small variety of information to be stored in a *.config file. More complex configuration, however, was most often accomplished with custom XML structures and custom parsing code. Such code could become quite complex, though and there are a variety of ways of differing performance to accomplish the same thing. With .NET 2.0, the days of writing your own (probably complicated, poorly-performing and tedious) code to manage custom XML configuration structures are over. The custom configuration capabilities of the built-in XML configuration subsystem in .NET 2.0 have been greatly revamped, boasting some extremely useful and time saving features. With relative ease, just about any XML configuration structure you might need is possible with a relatively minimal amount of work. In addition, deserialization of the XML in a *.config file can always be overridden. This allows any XML structure necessary to be supported without losing the other advanced features of .NET 2.0 configuration. Exposing DetailsThis article continues a series on .NET 2.0 configuration. This article aims to expose all of the details and inner workings of the .NET 2.0 Configuration framework to allow developers to better utilize the extensive capabilities provided. If you are new to .NET 2.0 configuration, or have not yet mastered concepts such as type validation and conversion, you should first read the previous articles, which can be found at the following links:
Please note that the code examples provided in this article are only meant for clarification of points made in the article. Compilable and downloadable code examples are provided in the first two articles of this series. The goal of this article, which is intentionally more detailed and advanced than the previous two, is not to provide compilable, runnable code examples. Rather, the goal is to expose the core underlying theory and important details of the .NET 2.0 Configuration framework. The desired result is that anyone interested in utilizing the .NET 2.0 configuration to it's fullest extent may do so after reading the information contained in this series of articles. Topics of ConfigurationWelcome to the third installment of the Mysteries of .NET 2.0 Configuration series. This article will cover the .NET 2.0 Configuration Framework in complete detail. I will be exposing little-known features, capabilities, quirks and internal workings of all aspects of the framework. Several architectural diagrams will also be presented to provide a visual anchor for the details to be discussed.
Configuration StructureMany forms of configuration are available for modern .NET applications. Binary data, INI files, databases, XML, even arbitrary text structures. Depending on the environment, type of application, usage factors, etc. your configuration storage needs may change. For the most part, however, most .NET applications (and the programmers who write them) just need the ability to store configuration... doesn't really matter how. Most configuration tends to be hierarchical and manual editing is usually desirable. This makes XML an ideal platform upon which to build a configuration storage framework. This also makes the .NET 2.0 configuration framework very appealing to the .NET developer. Hierarchical Configuration"Hierarchical" is the name of the game when it comes to the .NET 2.0 Configuration framework and in more ways than one. Aside from being an application of XML and providing a hierarchical medium within which to store actual configuration settings, the .NET 2.0 configuration framework is hierarchical in it's own right. Depending on the application context, a natural hierarchical order of multiple configuration files exists and are merged together when configuration is requested by code. This hierarchical structure to configuration files allows settings to be applied at various levels, from the entire machine, to the application and even down to the individual user or resource being requested. The hierarchical structure of the .NET 2.0 Configuration framework can be seen in the diagram below. A few important points to note about this diagram are the order in which configuration files are merged and the contexts within which configuration is available. Depending on the context, the nature of what can be configured and how those settings are merged can differ in important ways. ContextsIn the .NET world, there are two primary types of applications: Windows Applications and Web Applications. Windows applications, or executables, have a relatively simple 4-layer configuration structure and merge process. Web applications, on the other hand, have a more complex structure for applying and merging configuration for specific locations. These two primary types of applications each create a configuration context with the rules that govern how configuration is applied. Independent of any context is machine-level configuration. Machine level configuration applies to any application that runs within the CLR, as well as the basic setup for all other configuration options that the .NET framework itself uses. It may come as a surprise to many, but the .NET framework uses the same configuration framework discussed in this series of articles. It may further surprise many that none of the configuration capabilities observed within the .NET framework are special or exclusive... you can do all of the same things. A quick examination of the machine.config file (found at %SYSTEMROOT%\Microsoft.NET\Framework\v2.0.50727\CONFIG) will show that this is where all of the "preconfigured" or "default" ConfigurationSectionGroup and ConfigurationSection elements are defined. Things like appSettings, connectionStrings, system.web, etc. Many default settings, such as section encryption providers, default ASP.NET membership, profile and role providers, etc. are applied here. In the same directory, you should also notice commented versions of this file, a default version, as well as several default web.config files for various security levels. While editing the machine.config file can be risky, it is also the only way to apply settings globally to every application. Below the machine level, configuration splits into the two available contexts... Exe and Web. The Exe context is available to any executable application. This includes Windows Forms, Windows Services and Console applications. Within this context, the core configuration infrastructure understands a total of four levels of configuration: Machine, Application, Roaming User and User. Each level is subsequently more specific in "context." Therefore each subsequent level can override settings defined higher up. It is important to note that Roaming User configuration is less specific than User configuration. This allows user-specific settings to reside on both a desktop and a laptop independent of each other, while roaming settings are shared between the two and available on both. The Web context is available only to applications that run within an ASP.NET host (does not necessarily have to be IIS... any ASP.NET host will do, including the VS2005 developer server or a custom web server that utilizes System.Web.Hosting.ApplicationHost). Unlike the Exe context, which is process- and user-dependant, the Web context is location dependant. Configuration may target specific website locations either explicitly as configured in a web.config file, or implicitly as multiple web.config files are merged. Configuration on a per-user basis is generally unavailable in the Web configuration context, even if a user is properly authenticated. MergingThe hierarchical nature of .NET 2.0 configuration provides a great level of flexibility, allowing specific users or locations to have their own configuration settings. However, those configuration settings are not isolated and duplicate settings made at a more specific level have the ability to override settings made at a less specific level. As can be seen in Figure 1, the most specific configuration files are merged into the less specific, with the most specific settings overriding the least specific. In the Exe context, User (or to be more precise, Local User) settings are most specific, followed by Roaming User (shared between two or more machines), Application and, finally, Machine. In the Web context, merging is a little more complex. In addition to multiple *.config files merging from subsequently more specific locations (i.e. \wwwroot\siteA\web.config is more specific than \wwwroot\web.config when merged), location-specific configuration can be defined directly within a single web.config file for an application. The nuances of web.config and location merging will be discussed later in this article and in greater detail. Configuration ArchitectureThe .NET 2.0 Configuration framework, while providing a quick, simple means of implementing custom configuration, is fairly complex in and of itself. The configuration framework provides not only the means to implement custom configuration, but also provides the means to implement custom data validation and conversion, custom providers, exposes hooks for custom serialization and deserialization and even allows configuration to be encrypted. Metadata about each configuration element is also exposed, providing details about the whats, wheres and hows of the data that was loaded. The architectural structure of the .NET 2.0 Configuration framework is displayed in Figure 2. The specifics of each element in the diagram will be the primary topic of this article as you read further. Hidden within this complicated looking diagram is an elegant, logical and efficient system for managing configuration. I have tried to organize the individual components of this architecture into logical groups, which form the core sections of this article:
Configuration ManagementThe first concept to be discussed in detail is also, logically, the first place you start when working with configuration in your projects. Configuration management, in the scope of this article, refers to finding configuration (either directly, or by mapping to specific configuration files), retrieving configuration sections and the storage of those sections during use. Configuration files may be loaded directly, providing additional capabilities and allowing specific *.config files (vs. those mapped by default) to be loaded. Mapping to specific configuration files differs depending on the context your operating under, but allows any configuration file to be loaded provided the necessary permissions to read from the resource are granted. ConfigurationManagerThe ConfigurationManager and subsequently the WebConfigurationManager, are the primary starting points for accessing .NET 2.0 configuration. The ConfigurationManager provides all of the core services required to retrieve configuration sections and the settings contained within them and also provides methods to allow configuration files to be explicitly loaded in the Exe context. The WebConfigurationManager provides additional methods to explicitly load configuration in the Web context, while also acting as a proxy for common ConfigurationManager methods. These two static classes are diagramed below for reference. The primary functionality provided by the ConfigurationManager class, There are two overloads of the
The solution to the above problem, which is one of the problems developers most frequently ask about, is
The above scenario, which appears to be frequent enough, allows multiple configuration files (specific to any assembly, not just the primary EXE) to be used at the same time. Despite the fact that // Assumes Library.dll and its corresponding Library.dll.config file exist,
// are referenced and properly colocated with the .exe
Console.WriteLine("App (before): " +
ConfigurationManager.AppSettings["test"]);
Console.WriteLine("Loading Library.dll.config...");
Configuration other =
ConfigurationManager.OpenExeConfiguration("Library.dll");
Console.WriteLine("App (after): " +
ConfigurationManager.AppSettings["test"]);
Console.WriteLine("Lib (after): " +
other.AppSettings.Settings["test"].Value);
// Expected output
App (before): application
Loading Library.dll.config...
App (after): application
Lib (after): library
<!-- Application.exe.config -->
<configuration>
<appSettings>
<add key="test" value="application" />
</appSettings>
</configuration>
<!-- Library.dll.config -->
<configuration>
<appSettings>
<add key="test" value="library" />
</appSettings>
</configuration>
Despite its misleading signature and name, The second method,
Remember that configuration is hierarchical and merged. When requesting roaming or local user configuration, that level up through machine.config are merged, resulting in the complete configuration accessible by your application for the given user level. An interesting consequence of this merging allows you to request roaming or local user configuration even when the User.config file does not exist. The Configuration object returned will contain a FilePath that does not exist and the HasFile property will be false. Any sections defined in higher configuration levels will still be accessible though and with the proper allowances, changes to those sections can be saved. Saving changes at a level that previously did not exist will create the appropriate User.config file. Roaming and local user configuration settings are an interesting beast. Some of the more subtle behaviors of .NET 2.0 configuration take shape (or rear their ugly heads, depending on how you look at it). In spite of their ugly heads, these subtle behaviors are another great reason to choose to use .NET 2.0 configuration over a custom solution. The .NET configuration framework provides some extensive security features that allow settings, sections and even section groups to be locked down (absolutely, or depending on what level they are being accessed from). Consider the following code: Configuration roamingCfg =
ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.PerUserRoamingAndLocal);
CustomSection customSection =
roamingCfg.GetSection("customSection") as CustomSection;
if (customSection == null)
{
customSection = new CustomSection();
roamingCfg.Sections.Add("customSection", customSection);
}
customSection.CustomValue = "local user value";
customSection.Save(ConfigurationSaveMode.Minimal);
This example should open the local User.config file and get (or create, if it does not exist) some In addition to those just discussed, several other methods exist to open configuration files. Unlike the The last method exposed by the ConfigurationManager class, WebConfigurationManagerThe ConfigurationManager class, while the first step to fully accessing your Exe context configuration, is woefully inadequate within the Web context. Unlike executable applications, web applications do not have a well identified local user for which User.config files may be created. In fact, there can be no user specific configuration at all in a web application. Despite that, web configuration can be an even more complex beast when location-specific configuration is considered. Location specific configuration and how to use the WebConfigurationManager to take full advantage of it is nearly an article in and of itself. A complete discussion of the ConfigurationFileMapThe ExeConfigurationFileMapThe string appData = Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData);
string localData = Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData);
ExeConfigurationFileMap exeMap =
new ExeConfigurationFileMap();
exeMap.ExeConfigFilename =
"C:\Application\Default.config";
exeMap.RoamingUserConfigFilename =
Path.Combine(appData, @"Company\Application\Roaming.config");
exeMap.LocalUserConfigFilename =
Path.Combine(localData, @"Company\Application\Local.config");
Configuration exeConfig =
ConfigurationManager.OpenMappedExeConfiguration(
exeMap, ConfigurationUserLevel.None);
Configuration roamingConfig =
ConfigurationManager.OpenMappedExeConfiguration(exeMap,
ConfigurationUserLevel.PerUserRoaming);
Configuration localConfig =
ConfigurationManager.OpenMappedExeConfiguration(exeMap,
ConfigurationUserLevel.PerUserRoamingAndLocal);
Console.WriteLine("MACHINE/EXE: " + exeConfig.FilePath);
Console.WriteLine(
"MACHINE/EXE/ROAMING_USER: " + roamingConfig.FilePath);
Console.WriteLine(
"MACHINE/EXE/ROAMING_USER/LOCAL_USER: " + localConfig.FilePath);
WebConfigurationFileMapThe ConfigurationIf the ConfigurationManager class is the first step along the Yellow Brick Road to the Emerald City of Configuration, then the After examining the diagram in Figure 5, you should see that the Configuration class exposes much more information and functionality than the ConfigurationManager does. Some of the information is the same, including Some features of the Configuration class that have not yet been discussed and which are not readily apparent, are made possible by the section and section group collections. In addition to allowing you to load and save existing configuration sections and section groups, you can also add and remove section groups. This is a powerful feature that allows a configuration file to be programmatically created using code. This can be very useful when working with roaming or local user configuration that requires settings not necessary for base application functionality. An important factor to note when creating sections this way. By default, sections may only be defined at the machine and exe levels. If you need to add a new configuration section, even if the section will only be used in a roaming or local user *.config file, you must add the section at the exe level first, then modify the section settings at the user level. Consider the following code: Configuration exeConfig =
ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.None);
if (exeConfig.GetSection("customSection") == null)
{
CustomSection section = new CustomSection();
section.SectionInformation.AllowExeDefinition =
ConfigurationAllowExeDefinition.MachineToLocalUser;
exeConfig.Sections.Add("customSection", section);
exeConfig.Save(ConfigurationSaveMode.Minimal);
}
Configuration userConfig =
ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.PerUserRoamingAndLocal);
CustomSection section =
userConfig.GetSection("customSection") as CustomSection;
section.SomeSetting = "some value";
userConfig.Save(ConfigurationSaveMode.Minimal);
Before the code above has executed for the first time, the EXE and user *.config files should look like this: <!-- EXE .config file -->
<configuration>
</configuration>
<!-- USER .config file -->
<!-- DOES NOT EXIST YET! -->
After the code above has executed for the first time, the exe and user *.config files should look like this: <!-- EXE .config file -->
<configuration>
<configSections>
<section name="test" type="Example.CustomSection, Example"
allowExeDefinition="MachineToLocalUser" />
</configSections>
</configuration>
<!-- USER .config file -->
<configuration>
<test>
<add key="key" value="value" />
</test>
</configuration>
The definition allowances (AllowDefinition and AllowExeDefinition) are an important factor when working with multiple levels of configuration. They are two of those "subtle behaviors" mentioned previously that can complicate working with .NET 2.0 configuration. Detailed explanations of definition allowances, as well as other settings that can have similar subtle effects, will be discussed in the Configuration Metadata section of this article. ContextInformationAn important property of the Configuration RepresentationConfiguration management, while the first step in accessing configuration, is far from being the most important component of the process. The ultimate goal of configuration is to represent setting structure and setting data in a simple, logical manner. The ultimate core of the .NET 2.0 configuration framework is the In previous articles in this series, we discussed how to create custom The primary purpose of the The .NET 2.0 configuration framework allows a configuration "specification" to be defined in one of two ways: declaratively and imperatively. The declarative method is accomplished by placing attributes on class properties that describe how that property corresponds to an element in an XML file. The imperative method is accomplished by predefining the properties programmatically in a static constructor. In the end, both methods accomplish the same thing, but how they accomplish it is quite different. The declarative method is generally considered the simpler of the two and requires less work on the programmers part. However, this simplicity of implementation comes at the cost of higher processing whenever a configuration element is refreshed. ConfigurationElementThe features of the ConfigurationElement class can generally be split into four groups: parsing (serialization and deserialization), configuration infrastructure, settings data and metadata. If you glance at Figure 6, you might also notice that the vast bulk of this class is non-public. Most of the methods and properties of the ConfigurationElement are only accessible to derivative classes, leaving only locking information, indexers and the IsReadOnly method public. The majority of the protected members are also marked as Sections dedicated to discussing parsing and configuration metadata can be found later in this article and defining & accessing configuration settings data has been discussed in previous articles and will not be reiterated here. While the final aspect of the ConfigurationElement class, infrastructure, is seldom needed, yet it can be the solution to those many small problems. The infrastructure methods of the ConfigurationElement we will be discussing are as follows:
Despite their woefully inadequate documentation, the The
While a theoretical discussion of how and when the Unlike the Init method, which may be called at any time in response to a variety of triggers, the The modified and read-only states are a fairly self-explanatory and common object state. In regards to the configuration framework, however, it helps to understand when modified and read-only states may be set and reset. The modified state is set true only by the The The The An element reset is a fundamental process that restores lock information to defaults and reapplies inherited locking information and sets property values to those previously loaded from the configuration file. A reset call to a ConfigurationSection will cascade the call to all child elements, etc. recursively, forcing all elements that make up the configuration section to be reset. For the most part, understanding how a Reset works in inconsequential. Configuration is generally used read-only, or when configuration must be read/write, most needs are basic. However, intercepting a Reset call and modifying it is often necessary when data stored in configuration properties is cached in a different form (i.e. an encoded string is decoded and cached upon request, requiring the cached copy to be dropped when the section is reset). The base workings of a configuration element reset should generally not need to be modified, but as in the case of dropping manually cached data, sometimes it must be augmented to ensure an element is fully and properly reset. The Reset method is called in only two instances... during an Unmerge operation, or during the various methods of creation of a configuration element. The Unmerge OperationThe Unmerge operation is another fundamental process that is performed when a configuration section is saved. You should remember from earlier in the article that configuration files are hierarchical, with more specific files merging with less specific files. This merging provides a single unified view of all configuration settings from the machine level all the way through a specific users configuration (or in the case of a web environment, through a specific web site or web application). Configuration sections defined in a lower level may have their settings changed in a higher level. Collections of configuration elements may have their children removed, changed, or even cleared by a higher level. When working with configuration in an application, this merged view makes consuming configuration a very simple task and removed a lot of the need for a complete understanding of where configuration settings come from. However, when the time comes to save configuration settings that have been modified during runtime, this merged view must be properly unmerged. Considering that, depending on which level of configuration your working with and saving, you could be adding, removing, clearing and changing settings that may or may not yet exist at the level your working at (yet should be saved back to the current level), the unmerge operation is fairly complex. The specific details of how an unmerge operation will not be discussed here (as I am not entirely clear on the precise workings myself), but suffice it to say, this process handles the separation of inherited settings from lower levels from the current settings of the working level. What is more important is the understanding that many of the methods described above are called during an unmerge, as current elements are often cloned, reset or initialized and repopulated with only the appropriate data that should be saved. What that data is depends on the ConfigurationSaveMode chosen when ConfigurationSectionThe ConfigurationSection class is a derivative of ConfigurationElement, meaning it may behave the same, containing its own attributes and child elements. In addition to inheriting all of the functionality and core behavior of a ConfigurationElement, ConfigurationSection adds its own features specific to a root node of a primary part of a configuration file. The only public feature that ConfigurationSection adds is the SectionInformation metadata property. This property, which will be discussed in detail later, provides some detailed information about a configuration section, including access to its raw XML. The ConfigurationSection adds a few new protected methods, including DeserializeSection and SerializeSection. The DeserializeSection method simply calls the base ConfigurationElementCollectionThe ConfigurationElementCollection, like ConfigurationSection, is also a derivative of ConfigurationElement. The ConfigurationElementCollection has been discussed in previous articles, so a detailed discussion of it will not be covered here. An examination of the class diagram should provide a clear view of the features this class provides to derivative collections, most notably the Base* methods. One method that should be noted is the Non-Element ContainersIn addition to the classes that directly correspond to configuration elements in a *.config file, there are also some basic organization classes that loosely correspond to section groups. Unlike a ConfigurationSection, a ConfigurationSectionGroup is not directly mapped to a configuration element after it has been loaded and is not directly saveable. A section group and the related child collections, are populated directly from a configuration record, which is a class that is part of a hidden framework of factories, records and various support types used to process raw XML data and generate configuration sections. Two other classes, ConfigurationSectionCollection and ConfigurationSectionGroupCollection, are processed in the same way as ConfigurationSectionGroup. Considering that the bulk of the code that processes the XML for these elements and populates them is hidden, a detailed discussion of them is probably not required. They simply provide an organizational structure, allowing configuration files to be cleaner and easier to maintain. Configuration MetadataConfiguration, if it requires a system such as that provided with .NET 2.0, is usually fairly complex. Such configuration is usually hierarchical, relational and fundamentally critical to supporting a flexible, reusable, easily maintainable architecture for one or more applications. Often, in such complex application architectures, more information is required about configuration besides just the configuration settings themselves. This additional information is either required to support proper implementation of the configuration itself, or possibly to support use of that configuration by the code that consumes it. Thankfully, the .NET 2.0 configuration framework is riddled with metadata, exposing every last conceivable detail about the configuration your working with. ContextInformationEarly in this article, I covered the basic hierarchical structures of configuration in two possible environments: configuration for an executable and configuration for a web application. These two configuration environments define the context within which configuration is loaded, merged, saved and unmerged. The context is more than just a context, however and specific details about the current configuration context can be accessed through the ContextInformation object. The ContextInformation object is exposed in only two locations... by the Configuration object and by the ConfigurationElement object (and all its derivatives). The ContextInformation class is simple, providing access to a more detailed context information object (HostingContext), as well as a flag indicating whether the current configuration object is the machine-level configuration. Note that Machine-level configuration is inherently context-less, existing and behaving the same in both the Exe context and the Web context. ExeContextThe ExeContext class is a very simple class, providing only information about the path of the current executable application and the current UserLevel for which the configuration object was loaded. This information will usually be known by the time a Configuration object was loaded, but if the context is accessed through a ConfigurationElement object, the ConfigurationUserLevel may not necessarily be known without accessing this object. WebContextThe WebContext class provides a bit more detail than the ExeContext, as the configuration hierarchy in a web environment can be more complex. Through the Web context, you have access to the site name, virtual path, application path and location sub path that the configuration object or element was loaded for. In addition, you have access to the WebApplicationLevel that the configuration file itself resides at. Depending on whether the configuration is being accessed by a root page, a page in a child directory, or from a child web application, the actual *.config file may reside at a higher or lower directory level than the code using that configuration. ConfigurationPropertyOf all metadata classes in the .NET 2.0 configuration framework, the ConfigurationProperty should be the most familiar to those who read the first two articles in this series. This class provides the means through which we define the actual settings that can be configured within a given configuration section and its child elements. Despite the fact that it is familiar, it should be clarified that the ConfigurationProperty is indeed metadata about a configuration element's settings and does not directly represent the XML stored in a *.config file. Aside from use in custom configuration classes to define what settings should be available, the ConfigurationProperty class is only used internally by the configuration framework. An interesting note about the ConfigurationProperty class is that it provides access to the only two non-metadata, non-configuration support types used in the .NET 2.0 configuration framework. Each configuration property may be associated with one type converter and validator, to facilitate conversion between natively typed data and a string and to validate the property value once it is in a native type. These two support types provide a great deal of flexibility in the configuration framework, enabling specific string structures to be stored in an XML file and later converted to native, possibly complex, .NET types upon deserialization (and back to strings upon serialization). ConfigurationElementPropertyOf all the metadata classes provided in the .NET 2.0 configuration framework, the ConfigurationElementProperty has to be the oddest. This class exposes only a single property, Validator, which one would think could have been exposed directly. Regardless of its oddity, the ConfigurationElementProprety provides a ConfigurationElement with direct access to the validator assigned to ensure that elements data is correct. During both deserialization and serialization, an elements validator is called to ensure that invalid data is not read or written at any time. The ConfigurationElementProperty is generally assigned during construction, when the internal configuration management framework reads a *.config file and generated ConfigurationElements from the defined properties. ElementInformationUnlike the ConfigurationElementProperty, ElementInformation provides some very useful metadata about a configuration element. To access the ElementInformation object you must reference the ConfigurationElement.ElementInformation property, as this is the only location it is exposed by the framework. This object provides some basic flags indicating whether the element is a collection element ( In addition to some basic flags and file information, the ElementInformation object provides two collections: The last collection exposed by ElementInformation, SectionInformationThe SectionInformation is another very useful metadata object. This object provides a considerable amount of allowance and locking information, as well as providing some interesting encryption and loading mechanisms. For the most part, the allowance and locking properties of this class can be set in XML using various attributes. Configuration section allowances and locking is an important concept that is used fairly extensively in machine.config and which may also be the root cause of many odd issues one may run into when changing inherited settings or programmatically editing them. SectionInformation common attributes discussion, coming soon! ProtectedConfigurationProviderEncrypted configuration sections, coming soon! CreditsBefore I finally close this series (or at the very least, these three articles...there may be more), I feel I must offer credit where it's due. I've researched .NET 2.0 configuration for at least a hear and a half and it's been great fun learning and writing these articles. None of this would have been possible, however, if it weren't for a little utility called Reflector, written by one Lutz Roeder. As someone who has to know all the details about everything, I would be completely lost in the world of .NET software development without this perfect little gem. It has offered me with a clearer, deeper understanding of the .NET framework than any amount of documentation could ever provide. Not to mention the fact that I could very well still be ignorant of the .NET 2.0 configuration framework to this day if I hadn't been poking around the .NET source code. Second, I would like to thank everyone who has posted questions to previous articles, or sent me questions through email. Many of your questions I was able to answer strait off from prior knowledge, but many others provided interesting challenges that forced me to dig deeper and deeper into the .NET 2.0 configuration source code to figure out the answer. Without the continual challenge to provide an answer, I would have never found them. With hope, this article will answer any future questions that may arise, but for those adventurous types who like to live on the fringe, I always welcome the company. I would also like to thank the CodeProject Staff for helping me maintain these articles and for their hard work editing them. I'm a very verbose individual and I can only imagine how much work it is to keep my ramblings up to snuff. ;) Work on these articles is sure to continue on into the future and I have to thank the CodeProject staff for being available to post future updates as well. I would also like to thank CodeProject editors for choosing both the first and second articles as Editors Choice when they were first posted. Their confidence in me as an author has helped inspire me to continue working on this series and there have been times when I wasn't sure I had the energy to continue. Finally, I must thank Microsoft for providing the .NET 2.0 configuration framework in the first place. After years of toiling in futile attempts to create my own reusable, simple, flexible, type-safe configuration frameworks, (and often restarting from scratch when a brick wall suddenly appears out of the mist) I no longer have to bother. The configuration framework provided by Microsoft with .NET 2.0 is wonderful and provides everything I could hope for in terms of configuring one application after another. The more I learn of the .NET 2.0 configuration framework, the more it shines as one of the most polished aspects of .NET 2.0 and definitely one of my favorite tools. Article Revision HistoryThis article is currently undergoing heavy, active editing. Be sure to check back often to see new sections and updates. During this active period, it is possible some content will be changed or sections rearranged. I apologize if that poses an inconvenience, but many people have been requesting this article. Considering the scope and detail of this article, I thought it best to post it in pieces as I refined and finalized sections.
| |||||||||||||||||||||||||||||||||||||||||||||||||||