Click here to Skip to main content
15,885,668 members
Articles / Web Development / ASP.NET
Tip/Trick

Managing Website Settings in a Web farm using Entity Framework

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
20 Jan 2015CPOL2 min read 11.7K   9   1
A way to manage Website settings in a web farm environment

Introduction

If you are used to storing various settings for your Website in Properties.Default.Settings, and you deploy your site to a Web farm, you may notice that programmatically updating those settings gets a little complicated.

Background

The Properties.Default.Settings settings end up getting written to the Web.config file, which can work fine for a Website running on a single machine, but in a Web farm environment, one instance of your site could be on a different machine or virtual machine. These machines would actually see a different Web.config, so synchronizing settings in this environment gets a bit more complicated.

So you might save your settings in the database. Then your site settings would be visible to all instances of your site as soon as they were committed. Adding new settings would require a change to the database and a database migration to make them available.

I wanted to use Entity Framework to store my settings, but I didn’t want to change the database when I added a new setting. I also wanted an interface similar to the old standard Properties.Default.Settings interface where I could set and retrieve values easily in a type safe manner. I had considered some fancy dynamic object scheme, but my settings really should be static properties because they don’t belong to any particular object. In short, I wanted to do something like:

C#
MailingAddress=SystemSettings.MailingAddress;

wherever I needed a system setting.

Using the Code

So I created the SystemSetting class as follows:

C#
public class SystemSettings
  {
  /// <summary>
  /// This little class allows arbitrary Name-Value pairs to be stored in the
  /// Entity Framework context. Values are stored as serialized binary.
  /// </summary>
  public class Setting
    {
    [Key]
    public string Name { get; set; }
    public byte[] BinaryValue { get; set; }

    /// <summary>
    /// Parameterless constructor for EF's use.
    /// </summary>
    public Setting()
      {
      }

    /// <summary>
    /// A short cut constructor for easy Setting creation
    /// </summary>
    /// <param name="Name">The Name to associate with the value.</param>
    /// <param name="Value">The value to store.</param>
    public Setting(string Name,object Value)
      {
      this.Name=Name;
      this.Value=Value;
      }

    /// <summary>
    /// This represents the Value the programmer wants to access.
    /// It is transparently converted from its binary from.
    /// </summary>
    public object Value
      {
      get
        {
        BinaryFormatter bf = new BinaryFormatter();
        using(MemoryStream ms = new MemoryStream(BinaryValue))
          {
          return bf.Deserialize(ms);
          }
        }
      set
        {
        BinaryFormatter bf = new BinaryFormatter();
        using(MemoryStream ms = new MemoryStream())
          {
          bf.Serialize(ms, value);
          this.BinaryValue=ms.GetBuffer();
          }
        }
      }
    }

  /// <summary>
  /// An EF context to use to access our Settings.
  /// </summary>
  private static MyContext _db = new MyContext();

  /// <summary>
  /// Update the storage with any setting changes.
  /// </summary>
  public static void Save()
  {
  _db.SaveChanges();
  }

  /// <summary>
  /// General purpose setting retrieval gets Values from the context, but will fall back to
  /// the good old Web.config file if it's not found (updating the database for the future).
  /// </summary>
  /// <param name="Name">The name of the setting to retrieve.</param>
  /// <returns>The Value associated with the Name as an object.</returns>
  protected static object Get(string Name)
    {
    Setting setting = _db.Settings.FirstOrDefault
             (s => s.Name == Name); // try to get it from the database
    if(setting != null)
      {
      return setting.Value;
      }
    if(Properties.Settings.Default.GetType().GetProperty(Name) == null)
    return null;    // Fall back if setting not found in Web.config
    object result = Properties.Settings.Default[Name]; // try to get it from Web.config
    if(result != null)
      { // put it in the database for future use
      setting=new Setting(Name, result);
      _db.Settings.Add(setting);
      _db.SaveChanges();
      return result;
      }
    return null;
    }

  /// <summary>
  /// General purpose Name-Value setter. Adds a setting to the database
  /// or updates one that is already there.
  /// </summary>
  /// <param name="Name">The Name of the Setting.</param>
  /// <param name="value">The Value of the setting.</param>
  protected static void Set(string Name, object value)
    {
    Setting setting = _db.Settings.FirstOrDefault(s => s.Name == Name);
    if(setting != null)
      setting.Value=value;
    else
      {
      setting=_db.Settings.Create();
      setting.Name=Name;
      setting.Value=value;
      _db.Settings.Add(setting);
      }
    }

    /// Add any number and type of setting like this example:
  public static double ScoreSheetPageLength
    {
    get
      {
      return (double)Get("ScoreSheetPageLength");
      }
    set
      {
      Set("ScoreSheetPageLength",value);
      }
    }
  }

You will also need to add this to your MyContext:

C#
public DbSet<SystemSettings.Setting> Settings { get; set; }

This code will fall-back and use the Properties.Default.Settings values in Web.config if it can't find the value in the database, so you can still deploy new settings the old way.

Points of Interest

When I went looking for solutions to the Website settings problem, I came across lots of articles on how you could use Web.config on a Web farm, but they all seemed to have complications or unwanted side effects, like restarting all instances of the Website to propagate changes. I hope you find this approach easier and effective.

History

  • 20th January, 2015: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) CogniDyne Research
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Humayun Kabir Mamun20-Jan-15 18:48
Humayun Kabir Mamun20-Jan-15 18:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.