0

First of all, I have been looking online for a Settings Design Pattern but I haven't been able to find a solution that works in my case or only cryptic answers like Use Dependency Injection.

The Problem

I have a large ASP .NET solution which is deployed to multiple production environments. Many decisions in code are taken based on settings which have been implemented in the Web.config file (a few hundreds). At the moment they are all accessed using the NameValueCollection ConfigurationManager.AppSettings. I want to move these settings in the database such that I can create a user interface and modify them more easily afterwards.

I would also prefer for the settings to remain accessible from the Web.config such that if something happens with the database, the application won't break.

Solution Architecture

Basically, the solution consists of the following projects:

  • UI (this is were the Web.config file resides)
  • BLL
  • DAL
  • Integration Project 1 (the application interracts with other applications. This project uses configuration settings like web addresses, authentication information etc. which at the moment are passed as parameters in the methods - which I personally find very ugly)
  • Integration Project 2
  • ...

The Code

The Setting class (the database table is practically the same). SettingType is an enum which defines the type of the Setting: Int, String, Bool and I defined for the UI.

public class Setting
{
    /// <summary>
    /// Gets or sets the identifier.
    /// </summary>
    /// <value>
    /// The identifier.
    /// </value>
    public int Id { get; set; }

    /// <summary>
    /// Gets or sets the name of the setting.
    /// </summary>
    /// <value>
    /// The name of the setting.
    /// </value>
    public string Name { get; set; }

    /// <summary>
    /// Gets or sets the type of the setting.
    /// </summary>
    /// <value>
    /// The type of the setting.
    /// </value>
    public SettingType Type { get; set; }

    /// <summary>
    /// Gets or sets the setting value.
    /// </summary>
    /// <value>
    /// The setting value.
    /// </value>
    public object Value { get; set; }

    /// <summary>
    /// Gets or sets the setting description.
    /// </summary>
    /// <value>
    /// The setting description.
    /// </value>
    public string Description { get; set; }

    /// <summary>
    /// Gets or sets the setting key.
    /// </summary>
    /// <value>
    /// The setting key.
    /// </value>
    public string Key { get; set; }

    /// <summary>
    /// Gets or sets the default value
    /// </summary>
    /// <value>
    /// The default value
    /// </value>
    public string Default { get; set; }
}

The SettingHelper class (which does everything at the moment, will split functionality in the future):

public static class SettingHelper
{
    // Settings collection
    private static List<Setting> _settings;


    /// <summary>
    /// Reloads the settings.
    /// </summary>
    /// <exception cref="System.Exception"></exception>
    public static void LoadSettings()
    {
        _settings = new List<Setting>();
        // Code which loads the settings from the database
    }

    private static Setting GetSetting(string key)
    {
        try
        {
            // if settings are not loaded, we reload them from the database
            if (_settings == null || _settings.Count == 0)
                LoadSettings();

            var value = from Setting setting in _settings
                        where setting != null && setting.Key == key
                        select setting;

            return value.FirstOrDefault();
        }
        catch (Exception)
        {
            return null;
        }
    }

    public static void SetSetting(string key, object newValue, int userID)
    {
        var currentSetting = GetSetting(key);

        if (currentSetting != null && !Convert.ToString(currentSetting.Value).ToUpper().Equals(Convert.ToString(newValue).ToUpper()))
        {
            // Code which updates the setting value
        }
    }

    // For the UI
    public static IEnumerable<Setting> GetAllSettings()
    {
        if (_settings == null)
            LoadSettings();
        return _settings;
    }

    // To change back swiftly to the Web.config in case there are errors in production - will remove in the future
    public static bool SettingsFromDataBase
    {
        get
        {
            if (HttpContext.Current.Application["SettingsFromDataBase"] == null)
                HttpContext.Current.Application["SettingsFromDataBase"] = ConfigurationManager.AppSettings["SettingsFromDataBase"];

            bool settingsFromDataBase;
            if (bool.TryParse(HttpContext.Current.Application["SettingsFromDataBase"] as string, out settingsFromDataBase))
                return settingsFromDataBase;
            else return false;
        }
    }

    public static T ObjectToGenericType<T>(object obj)
    {
        if (obj is T)
        {
            return (T)obj;
        }
        else
        {
            try
            {
                return (T)Convert.ChangeType(obj, typeof(T));
            }
            catch (InvalidCastException)
            {
                return default(T);
            }
        }
    }

    public static T GetSetting<T>(string key)
    {
        if (SettingsFromDataBase)
        {
            var setting = GetSetting(key);
            return setting != null
                ? ObjectToGenericType<T>(setting.Value)
                : ObjectToGenericType<T>(ConfigurationManager.AppSettings[key]);
        }

        if (HttpContext.Current.Application[key] == null)
            HttpContext.Current.Application[key] = ConfigurationManager.AppSettings[key];

        return (T)HttpContext.Current.Application[key];

    }

    // The actual settings which will be used in the other projects
    public static string StringSetting
    {
        get { return GetSetting<string>("StringSetting"); }
    }

    public static bool BoolSetting
    {
        get { return GetSetting<bool>("BoolSetting"); }
    }

Note: Likely there are improvements to this class using Reflection, but I would prefer to avoid it (apart from that conversion).

Questions

At the moment the SettingHelper resides in the UI project (in order to be able to access the settings from the Web.config). How can I transmit these settings in the BLL or Integration projects?

Also, do you think this implementation with all the settings stored in a static list is the way to go? It feels unoptimal.

Any help or discussion on improvements is appreciated. Thank you.

Alexandru Popa
  • 166
  • 3
  • 18
  • 1
    The model code needs to live in a common project that can be referenced by the other projects that depend on it. The helper implementation should not be static and should have a backing abstraction that, as suggested by your readings, should be injected into dependents. – Nkosi Apr 10 '18 at 11:06
  • @Nkosi, The problem with using a common project is that I can't access the settings using ConfigurationManager.AppSetting and I want the settings to default to that in case a database/cast error happens. – Alexandru Popa Apr 10 '18 at 11:11
  • Hence the use of abstractions. You are focusing too heavily on implementation concerns. – Nkosi Apr 10 '18 at 11:12
  • @Nkosi, Ok, I'm still not very clear how I will go about defaulting them to the web.config when I'm in a different project, but I will try to write some interfaces and come back. Thanks. – Alexandru Popa Apr 10 '18 at 11:18

0 Answers0