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:
MailingAddress=SystemSettings.MailingAddress;
wherever I needed a system setting.
Using the Code
So I created the SystemSetting
class as follows:
public class SystemSettings
{
public class Setting
{
[Key]
public string Name { get; set; }
public byte[] BinaryValue { get; set; }
public Setting()
{
}
public Setting(string Name,object Value)
{
this.Name=Name;
this.Value=Value;
}
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();
}
}
}
}
private static MyContext _db = new MyContext();
public static void Save()
{
_db.SaveChanges();
}
protected static object Get(string Name)
{
Setting setting = _db.Settings.FirstOrDefault
(s => s.Name == Name);
if(setting != null)
{
return setting.Value;
}
if(Properties.Settings.Default.GetType().GetProperty(Name) == null)
return null;
object result = Properties.Settings.Default[Name];
if(result != null)
{
setting=new Setting(Name, result);
_db.Settings.Add(setting);
_db.SaveChanges();
return result;
}
return null;
}
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);
}
}
public static double ScoreSheetPageLength
{
get
{
return (double)Get("ScoreSheetPageLength");
}
set
{
Set("ScoreSheetPageLength",value);
}
}
}
You will also need to add this to your MyContext
:
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