Lifecycle Profile Settings
A simple mechanism for partitioning settings for development, acceptance, and production.
Introduction
This article demonstrates a simple solution to using different Property.Settings
for applications in development, acceptance, and production environments. This can be extended for using the same code base for different clients where the connection strings and other settings may be different.
Background
Visual Studio (and the .NET Framework) offers a convenient solution to adding and using application scope variables via the Settings on the Project Property pages. When different environments are configured for lifecycle stages (development, acceptance testing, and production deployment), the settings may need to be constantly changed at these stages. Connection strings and shared resource locations are the obvious settings, among others. This solution promotes the use of separate XML data files for each of the profiles (development, acceptance, and production), and the process of promoting the code base prior to deployment is as simple as changing one setting. In fact, I have found it easier to manage if the only settings in the Settings Designer are the location of the current profile file and any connection strings (as settings) used by the designers or code-behind. All of the other settings can sit in the XML data file, and be accessed via the included library API.
Using the code
The included solution has a library, a quick and dirty settings designer (I find the raw XML is just as easy), and two test harnesses. C# and VB.NET test harnesses have been supplied because the code-behind of the settings file in C# does not enumerate the built-in events.
The steps for using the test harnesses:
- From the Settings Designer (Project > Properties > Settings), assign the setting
runtimeSettingsFile
to either Profile1.xml or Profile2.xml. - Run the project.
- Note the data entries and the border color of the form.
- Close the form.
- From the Settings Designer, assign the setting
runtimeSettingsFile
to either Profile1.xml or Profile2.xml (not the last setting). - Run the project.
- Notice the data entries and border color have changed to reflect the values in the XML file. Although very simple, the two
runtimeSettings
represent different values used in development, acceptance, or production.
The steps for using the library from your project:
- Add a reference from your project to the library emx.Properties
- Create XML data files with all of the settings needed for each environment
- Set the properties of these XML files (BuildAction=Content, Copy to Output Directory=Copy, if newer)
- Add the following code to the Settings.vb or Settings.cs file (from Project > Properties > Settings > View Code)
- Add the setting
runtimeSettingsFile
with the name of the current XML file in the Settings Designer - Run your project
// C#
using emx.Properties;
using System;
namespace emx.Properties.TestHarnessCS.Properties {
// This class allows you to handle specific events on the settings class:
// The SettingChanging event is raised before a setting's value is changed.
// The PropertyChanged event is raised after a setting's value is changed.
// The SettingsLoaded event is raised after the setting values are loaded.
// The SettingsSaving event is raised before the setting values are saved.
internal sealed partial class Settings {
public Settings() {
SettingsLoaded += SettingsLoadedHandler;
}
private void SettingsLoadedHandler(object sender,
System.Configuration.SettingsLoadedEventArgs e)
{
// build the settings filename with the value assigned
// in Settings : System.Configuration.ApplicationSettingsBase
string s_xmlFile = System.IO.Path.Combine(_
System.Windows.Forms.Application.StartupPath, _
this["runtimeSettingsFile"].ToString());
if (!System.IO.File.Exists(s_xmlFile)){
// under normal circumstances this
// should only happen in development
System.Windows.Forms.MessageBox.Show(
"File does not exist:\n" + s_xmlFile);
}else{
try{
// set the global property
// to a new instance of the RuntimeSettings
emx.Properties.Globals.currentRuntimeSettings =
new RuntimeSettings(s_xmlFile);
// set any paired settings back
// into the Application Properties Settings
emx.Properties.Globals.currentRuntimeSettings.assignSettings(this);
}catch (RuntimeSettingsException ex){
// under normal circumstances this
// should only happen in development
System.Windows.Forms.MessageBox.Show(ex.Message +
"\nkey = " + ex.key +
"\ndataType = " + ex.dataType);
}
}
}
}
}
VB.NET:
//
' VB.NET
Namespace My
'This class allows you to handle specific events on the settings class:
' The SettingChanging event is raised before a setting's value is changed.
' The PropertyChanged event is raised after a setting's value is changed.
' The SettingsLoaded event is raised after the setting values are loaded.
' The SettingsSaving event is raised before the setting values are saved.
Partial Friend NotInheritable Class MySettings
Private Sub MySettings_SettingsLoaded(ByVal sender As Object, _
ByVal e As System.Configuration.SettingsLoadedEventArgs) _
Handles Me.SettingsLoaded
' build the settings filename with the value assigned
' in Settings : System.Configuration.ApplicationSettingsBase
Dim s_xmlFile As String = System.IO.Path.Combine(_
System.Windows.Forms.Application.StartupPath, _
Me("runtimeSettingsFile").ToString())
If Not System.IO.File.Exists(s_xmlFile) Then
' under normal circumstances this should only happen in development
System.Windows.Forms.MessageBox.Show("File does" & _
" not exist:\n" & s_xmlFile)
Else
Try
' set the global property to a new instance of the RuntimeSettings
emx.Properties.Globals.currentRuntimeSettings = _
New RuntimeSettings(s_xmlFile)
'[ set any paired settings back
' into the Application Properties Settings
emx.Properties.Globals.currentRuntimeSettings.assignSettings(Me)
Catch ex As RuntimeSettingsException
' under normal circumstances this should only happen in development
System.Windows.Forms.MessageBox.Show(ex.Message + _
"\nkey = " & ex.key & _
"\ndataType = " & ex.dataType)
End Try
End If
End Sub
End Class
End Namespace
Points of interest
Mechanisms for updating the values of settings (connection strings included) via the Settings code-behind are discussed frequently, but I have not seen a generic component/library that manages this process. The SettingsLoaded
event is used because it is fired before any values accessed via the Settings
object are used. I am sure this process can be optimized possibly by using the SettingsBase
class instead of the strongly typed Settings
class.
The following class diagram annotates some of the features:
History
- 2009-01-08 - Minor layout edits.
- 2009-01-07 - Article submitted.