VC++ Cures for .NET Configuration Change Headaches

Most developers have experienced the following scenario (or something similar): A few customers have reported an intermittent bug that can't be reproduced in the test lab. The bug is a real pain for the team responsible for that area of functionality, and it overshadows the good work they have done. Finally, a customer with the bug calls into the help desk and offers to do some diagnostics with the team. The lead developer talks the customer through the exercise of locating and opening the application's configuration file to determine where the log output is being sent. The customer tells the developer that logging has been turned off to save disk space. Turning logging back on, says the developer, requires the customer to restart the application. After restarting, the customer happily informs the developer that the problem seems to have disappeared.

The inability of the .NET configuration file to reload itself is a major headache in scenarios like this. The only time that the .NET Framework will reload configuration settings is when a new application domain is created. This is the technique ASP.NET uses to pick up new configuration settings, and the client/server model of ASP.NET makes shutting down an application domain and starting a new one a feasible and relatively simple process. For client-side applications that have a longer-term requirement to store data in memory, it is generally not feasible to move all the required data from one application domain to another simply to pick up new configuration settings. Even if it were possible, the complexity added to the code would be prohibitive.

Given these issues, why didn't Microsoft simply "fix" the problem in version 2 of the .NET Framework? Even if you ignore the argument about whether this would be a breaking change, and if so, whether it would be a worthwhile one, Microsoft's current position is that this behavior is "by design" and doesn't need fixing. Many developers, including myself, disagree with this position, and we've been actively lobbying for a change since the issue was raised recently by a CLR developer on his blog.

The best compromise is for the CLR team to add a new attribute to the appSettings element (called something like "reloadOnChange") that developers could use to instruct the Framework to reload the appSettings member if they observe a change in the configuration file. It is hard to see a problem with this compromise (other than the fact that someone has to implement it). The new behavior is opt-in only, so no current application could possibly be broken by its addition and no developer could get caught in tangles by unexpectedly getting a different set of configuration settings during an application's execution.

Given that the CLR team has not taken the reloadOnChange suggestion, what options exist for a Visual C++ developer to retrieve the current rather than the cached version of configuration settings?

Option 1. Revert to the Registry

In many ways, this is the simplest and most sensible option. The Windows Registry is a mature and fast option for storing a user's configuration data. The main disadvantage is that the Registry is not the "way of the future" in regard to storing application data, and it also requires the use of elevated privileges to read and write Registry settings. The Registry even supports a handy API (RegNotifyChangeKeyValue) that raises a Win32 event when a Registry key is changed in any way. RegNotifyChangeKeyValue is a native function, but thankfully Visual C++ makes calling these very simple.

Option 2. Reinvent the Wheel

The most naive and brute-force approach is to simply create a new XML-based configuration file that stores configuration data, and then build a new assembly that provides similar functionality to the types in System.Configuration. This approach has several problems. For starters, it involves a lot of work, and it requires the management of another dependency during deployment and operations. If this approach achieved wide-spread adoption, the lives of operations staffs would become exceedingly difficult as the number of different configuration files from component and application vendors would explode.

Option 3. Filewatcher and Reread

Detecting changes made to the configuration file is relatively simple using the managed System.IO.FileSystemWatcher class or the unmanaged ReadDirectoryChangesW function. Both of these require Windows NT4 and above (FileSystemWatcher is implemented using ReadDirectoryChangesW), but as the Windows 9x family of operating systems continues to decrease in its user base, this limitation is not overly serious, particularly for those targeting the corporate environment.

To get the name of the configuration file, the application domain's setup information needs to be queried as follows:

String* configFileName = AppDomain::CurrentDomain->
                         SetupInformation->ConfigurationFile;

The runtime host can specify any arbitrary file for use as the configuration file, which means that it is vital to check this property rather than rely on the typical app.config or web.config names.

Once a change has been detected in the configuration file, retrieving the new settings can be accomplished by either loading a new application domain or using an XML parser to manually reread the required values. For configuration data stored in the appSettings element, there is no clean way to update the data in the ConfigurationSettings class. The static AppSettings property has no public set_ method, and the NameValueCollection collection returned by the property is read-only.

The managed reflection API or raw memory access can be used to set the new values in the original collection, but both of these techniques rely on implementation details. Also, new versions and service packs of the .NET Framework can easily break the repopulation code.

The best option for implementing the reload in a robust manner is simply to create a new class that originally reads the settings from the ConfigurationSettings class, and then repopulates the data when the values change. A more advanced option is to use a custom configuration file handler. (I will explore this option in more depth next month.)

Option 4. Enterprise Library Configuration Block

One of the simplest off-the-shelve options for reloading configuration data is the Configuration Application Block, which forms part of Microsoft's Enterprise Library. The Configuration Application Block is designed to allow the underlying storage of the configuration data to be decoupled from the application code that accesses it. One of the key advantages of the Configuration Application Block is that it has inbuilt support for reloading configuration data when the underlying data store changes.

The Enterprise Library, which ships as binaries or as C# and VB .NET code, includes a configuration console that can be used to set up an application's use of any of the application blocks. The console can create an application configuration file that stores only the metadata about where the configuration data is held. The XML storage provider and XML serializer transformer that are part of the Configuration Application Block can manage the serialization and deserialization of any type that is compatible with the inbuilt XML serialization. The typical usage scenario is to create a class that holds all the configuration data an application needs, generate a XML file that represents the serialized form of this class, and then reference this file from the main application configuration file. Once the basic schema has been created, the Configuration Application Block then handles all the loading and storing of this data. It also raises standard .NET events when the configuration data is changed.

To provide a quick example, the following class could be used to hold an applications configuration data:

__gc public class ConfigData
{
   private:
      int m_configData1;
      String* m_configData2;
   public:
      ConfigData(){}

   __property int get_ConfigData1() { return m_configData1; }
   __property void set_ConfigData1(int value)
   { m_configData1 = value; }


   __property String* get_ConfigData2() { return m_configData2; }
   __property void set_ConfigData2(String* value)
   { m_configData2 = value; }
}

For an application that has been set up to use the Configuration Application Block, loading the data from the XML file (or loading the data from any other store that has been configured for use in production) can be accomplished with the following code:

ConfigurationManager::ConfigurationChanged
+= new ConfigurationChangedEventHandler(
   this, &Subscriber::OnConfigurationChanged);

ConfigData* config = dynamic_cast<ConfigData*>
   (ConfigurationManager::GetConfiguration
   ("ConfigSetttings"));

This snippet of code also subscribes to events that are raised by the Block. Once this change has been detected, the new configuration data can simply be loaded into the existing object:

void OnConfigurationChanged(Object* sender,
                            ConfigurationChangedEventArgs* args)
{
   config= dynamic_cast<ConfigData*>
   (ConfigurationManager::GetConfiguration("ConfigSetttings"));
}

More to Come. Stay Tuned!

Next month, I will cover the use of custom configuration handlers that enable the reloading of configuration settings without the need to migrate applications to the Enterprise Library.

About the Author

Nick Wienholt is an independent Windows and .NET consultant based in Sydney, Australia. He is the author of Maximizing .NET Performance from Apress, and specializes in system-level software architecture and development with a particular focus on performance, security, interoperability, and debugging. Nick can be reached at NickW@dotnetperformance.com.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

Most Popular Programming Stories

More for Developers

RSS Feeds