I have a problem when I want to create a settings structure for my application, which should be as optimal as possible in terms of localization, expansion and grouping. I want to group settings for each type of entity (you can think of it as grouping settings on a controller). Settings will be displayed to the user, so that each parameter needs a good name and description, which should be localized. New settings will be introduced by developers, and recompilation will be required.
What I came up with is a class that provides parameters as static properties, so they are easily accessible for use in the entire application in a static way. Settings are loaded when the class is first created (what happens when you request settings), and I use the database to store the settings and use reflection to assign them to the appropriate properties at runtime.
Looks like this
public class FirmSettings { private static IFirmSettingsRepository _repository { get; set; } public static bool ShowInvoicePaymentDetails { get; set; } public static bool ShowInvoiceDiscountValue { get; set; } public static bool ShowDocumentComment { get; set; } public static bool ShowDocumentTaxStatement { get; set; } public static bool ShowDocumentAuthor { get; set; } #region Constructors /// <summary> /// Initializes a new instance of the <see cref = "FirmSettings" /> class. /// </summary> static FirmSettings() { Load(); } #endregion #region Load Settings public static void Load() { _repository = MvcApplication.Container.Get<IFirmSettingsRepository>(); Type settingsType = typeof (FirmSettings); //------------------------------------------------------------ // Enumerate through individual settings nodes //------------------------------------------------------------ StringDictionary dic = _repository.LoadSettings(); if (dic == null) { Save(); // prepares the settings with blank settings dic = _repository.LoadSettings(); // reload } foreach (string key in dic.Keys) { //------------------------------------------------------------ // Extract the setting name/value pair //------------------------------------------------------------ string name = key; string value = dic[key]; //------------------------------------------------------------ // Enumerate through public properties of this instance //------------------------------------------------------------ foreach (PropertyInfo propertyInformation in settingsType.GetProperties(BindingFlags.Public | BindingFlags.Static)) { //------------------------------------------------------------ // Determine if configured setting matches current setting based on name //------------------------------------------------------------ if (propertyInformation.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) { //------------------------------------------------------------ // Attempt to apply configured setting //------------------------------------------------------------ try { if (propertyInformation.CanWrite) { propertyInformation.SetValue(typeof (FirmSettings), Convert.ChangeType(value, propertyInformation.PropertyType, CultureInfo.CurrentCulture), null); } } catch { // TODO: Log exception to a common logging framework? } break; } } } // perform resave if there are any new settings Save(); } #endregion #region Save settings /// <summary> /// Saves the settings to disk. /// </summary> public static void Save() { StringDictionary dic = new StringDictionary(); Type settingsType = typeof (FirmSettings); //------------------------------------------------------------ // Enumerate through settings properties //------------------------------------------------------------ foreach (PropertyInfo propertyInformation in settingsType.GetProperties(BindingFlags.Public | BindingFlags.Static)) { //------------------------------------------------------------ // Extract property value and its string representation //------------------------------------------------------------ object propertyValue = propertyInformation.GetValue(typeof (FirmSettings), null); string valueAsString; //------------------------------------------------------------ // Format null/default property values as empty strings //------------------------------------------------------------ if (propertyValue == null || propertyValue.Equals(Int32.MinValue) || propertyValue.Equals(Single.MinValue)) { valueAsString = String.Empty; } else { valueAsString = propertyValue.ToString(); } //------------------------------------------------------------ // Write property name/value pair //------------------------------------------------------------ dic.Add(propertyInformation.Name, valueAsString); } _repository.SaveSettings(dic); } #endregion }
Each setting is stored in the database as a lower version of the property name (we ignore the case for loading). The same goes for the localization string that will be stored, for example, FirmSettings_ShowDocumentTaxStatement_Title and FirmSettings_ShowDocumentTaxStatement_Desc . (Convention)
However, this approach does not solve the grouping. The user interface will require some kind of grouping of settings, so the account settings will be displayed in the group. I can imagine a prefix for certain parameters, and then visualize it based on the prefix (another convention).
Do you like this approach? If not, how do you do it? There are many conventions in this approach and that bothers me a bit.
source share