I needed to write a settings class for my web app; ideally this class should read its settings from a file (and be able to save them back to the file), and have only 1 instance per application - all threads read the same object, so that the settings only need to be loaded from the file when the app runs for the first time.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;
using System.Security.Cryptography;
using System.Configuration;
using System.Xml.Serialization;
using System.Web.Caching;
using System.IO;
namespace Secure_Password_Repository.Settings
{
[Serializable]
public sealed partial class AppSettings
{
private static readonly XmlSerializer serial = new XmlSerializer(typeof(AppSettings));
private static AppSettings instance;
private static Object thisLock = new Object();
private static void serializeToDisk(string key, CacheItemUpdateReason reason, out Object expensiveObject, out CacheDependency dependency, out DateTime absoluteExpiration, out TimeSpan slidingExpiration)
{
string filename = HttpContext.Current.Server.MapPath("settings.xml");
expensiveObject = instance;
dependency = new CacheDependency(filename);
absoluteExpiration = Cache.NoAbsoluteExpiration;
slidingExpiration = Cache.NoSlidingExpiration;
using (StreamWriter sw = new StreamWriter(filename))
{
serial.Serialize(sw, instance);
}
}
public static void Save()
{
string filename = HttpContext.Current.Server.MapPath("settings.xml");
using (StreamWriter sw = new StreamWriter(filename))
{
serial.Serialize(sw, instance);
}
}
public static AppSettings Default
{
get
{
Cache cache = HttpRuntime.Cache;
string filename = HttpContext.Current.Server.MapPath("settings.xml");
if (cache[filename] != null) {
return (AppSettings)cache[filename];
}
lock (thisLock)
{
if (cache[filename] != null) {
return (AppSettings)cache[filename];
}
using (StreamReader sr = new StreamReader(filename))
{
instance = (AppSettings)serial.Deserialize(sr);
cache.Insert(filename, instance, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration);
if (instance.EncryptionKeySalt == "")
{
//random number generator
RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
//create 20 random bytes for the salt
byte[] CryptoBytes = new byte[40];
rngCsp.GetBytes(CryptoBytes);
rngCsp.Dispose();
instance.EncryptionKeySalt = Convert.ToBase64String(CryptoBytes);
}
return (AppSettings)cache[filename];
}
}
}
}
public string LogoImage{ get; set; }
public string DefaultAccountRole{ get; set; }
public string SMTPServerAddress{ get; set; }
public string SMTPServerUsername{ get; set; }
public string SMTPServerPassword{ get; set; }
public string EncryptionKeySalt{ get; set; }
}
}
I'm just curious: is this "good code"? Are there any potential pitfalls I'm not seeing? is there anything I could be doing better?