Created
October 12, 2011 11:12
-
-
Save StevePy/1280939 to your computer and use it in GitHub Desktop.
Settings Refresh Extension
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Linq; | |
using System.Reflection; | |
using System.Configuration; | |
using System.Diagnostics; | |
namespace Spynical.Settings | |
{ | |
/// <summary> | |
/// Helper extension class for refreshing Settings from their associated .config file. | |
/// </summary> | |
/// <remarks> | |
/// This method allows DLLs (i.e. Plugins) to manage their own settings in .dll.config | |
/// files and have those settings override the attributed default settings by placing | |
/// the modified configuration file in the directory and calling the Reload() method. | |
/// Eg: Settings.Default.Reload(); | |
/// This also allows executables to reload their modified configuration files at runtime. | |
/// </remarks> | |
public static class SettingsExtensions | |
{ | |
private static Action<string, TraceLevel> _resultListener; | |
/// <summary> | |
/// Initializes a results listener so that applications can listen in on how the | |
/// settings refresh is going and log or record the operation. | |
/// </summary> | |
/// <param name="resultListener">Listener action delegate used to call back with status messages and/or errors.</param> | |
public static void InitializeListener( Action<string, TraceLevel> resultListener ) | |
{ | |
_resultListener = resultListener; | |
} | |
/// <summary> | |
/// Extension method on Settings to refresh their values from a configuration file | |
/// if present. | |
/// </summary> | |
/// <param name="settings">Settings object being reloaded.</param> | |
/// <remarks> | |
/// To refresh settings from file, place the .dll.config or .exe.config file in the | |
/// same folder as the dll/exe with settings to be refreshed. | |
/// Note that if a dll has a .config file and settings within its parent .exe.config file | |
/// settings in the .dll.config will override any settings in the .exe.config. This | |
/// method will not reload dll settings from parent .exe.config files. | |
/// </remarks> | |
public static void Refresh( this ApplicationSettingsBase settings ) | |
{ | |
var configName = string.Format( "{0}.config", settings.GetType().Assembly.Location ); | |
fireResultListener( string.Format( "Refreshing settings from {0}({1})", Path.GetFileName( configName ), configName ) ); | |
var configSectionName = string.Format( "{0}.Settings", settings.GetType().Namespace ); | |
if (!File.Exists( configName )) | |
{ | |
fireResultListener( "No configuration file found. Nothing to do." ); | |
return; | |
} | |
try | |
{ | |
// Look for a valid .config file and open if available. | |
var configuration = ConfigurationManager.OpenMappedExeConfiguration( new ExeConfigurationFileMap { ExeConfigFilename = configName }, ConfigurationUserLevel.None ); | |
if (configuration == null || !configuration.HasFile) | |
{ | |
fireResultListener( "Configuration file found, but could not be parsed.", TraceLevel.Warning ); | |
return; | |
} | |
var applicationSettingsSection = configuration.GetSectionGroup( "applicationSettings" ); | |
if (applicationSettingsSection == null) | |
{ | |
fireResultListener( "Application settings section not found in configuration.", TraceLevel.Warning ); | |
return; | |
} | |
// DefaultSettingValueAttribute denotes setting properies we'll be interest in. | |
var properties = settings.GetType().GetProperties( BindingFlags.Public | BindingFlags.Instance ) | |
.Where( p => p.GetCustomAttributes( typeof( DefaultSettingValueAttribute ), false ).Any() ); | |
foreach (var propertyName in properties.Select( p => p.Name )) | |
{ | |
try | |
{ | |
var value = ((ClientSettingsSection) applicationSettingsSection.Sections[configSectionName]).Settings.Get( propertyName ); | |
if (value != null) | |
{ | |
fireResultListener( string.Format( "Updating setting from: \"{0}\" to: \"{1}\".", settings[propertyName], value.Value.ValueXml.InnerText ) ); | |
if (settings[propertyName] is Guid) | |
settings[propertyName] = Guid.Parse( value.Value.ValueXml.InnerText ); | |
else if (settings[propertyName] is TimeSpan) | |
settings[propertyName] = TimeSpan.Parse( value.Value.ValueXml.InnerText ); | |
else if (settings[propertyName] is System.Drawing.Color) | |
settings[propertyName] = System.Drawing.Color.FromName( value.Value.ValueXml.InnerText ); | |
else | |
settings[propertyName] = Convert.ChangeType( value.Value.ValueXml.InnerText, settings[propertyName].GetType() ); | |
} | |
} | |
catch (Exception ex) | |
{ | |
fireResultListener( ex ); | |
} | |
} | |
} | |
catch (Exception ex) | |
{ | |
fireResultListener( ex ); | |
} | |
} | |
/// <summary> | |
/// Attempts to raise the result listener with the provided message and trace level. | |
/// </summary> | |
/// <param name="message">Message to notify the listener about.</param> | |
/// <param name="level">A trace level for the type of information. (defaults to "info")</param> | |
private static void fireResultListener( string message, TraceLevel level = TraceLevel.Info ) | |
{ | |
if (_resultListener != null) | |
{ | |
try | |
{ | |
_resultListener( message, level ); | |
} | |
catch | |
{ } // Ignore any exception raised by the listener. | |
} | |
} | |
/// <summary> | |
/// Notifies the result listener of any exception. | |
/// </summary> | |
/// <param name="ex">Exception to notify the listener about.</param> | |
/// <remarks> | |
/// The exception is raised as an "error" trace level. | |
/// </remarks> | |
private static void fireResultListener( Exception ex ) | |
{ | |
fireResultListener( ex.ToString(), TraceLevel.Error ); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment