Sometimes you want to get rid of those settings kept in the traditional web.config or app.config file. You want more fine grained control over the deployment of your settings entries and separated data design. Or the requirement is to enable adding new entries at runtime.
Here are two very short, but fully functional implementations:
- The strongly typed version and
- The object oriented version.
Let's start with the strongly typed version. The advantage here are the strongly typed settings names and values. There is no risk of intermixing names or data types. The disadvantage is that more settings have to be coded, cannot be added at runtime.
using System;
using System.Text;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
namespace Common
{
public class AppSettings
{
public static readonly string SettingsFilePath = new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName + @"\AppSettings.xml";
#region Get/Set
public static int GetSetting1() {
var appSettings = LoadAppSettings();
return appSettings.Setting1;
}
public static void SetSetting1(int value) {
var appSettings = LoadAppSettings();
appSettings.Setting1 = value;
SaveAppSettings(appSettings);
}
public static bool GetSetting2() {
var appSettings = LoadAppSettings();
return appSettings.Setting2;
}
public static void SetSetting2(bool value) {
var appSettings = LoadAppSettings();
appSettings.Setting2 = value;
SaveAppSettings(appSettings);
}
public static string GetSetting3() {
var appSettings = LoadAppSettings();
return appSettings.Setting3;
}
public static void SetSetting3(string value) {
var appSettings = LoadAppSettings();
appSettings.Setting3 = value;
SaveAppSettings(appSettings);
}
#endregion
#region Load/Save settings
static Settings LoadAppSettings() {
var fiFile = new FileInfo(SettingsFilePath);
if (!fiFile.Exists || fiFile.Length <= 3) { // UTF-8 preamble.
return new Settings();
}
using (var reader = new XmlTextReader(SettingsFilePath)) {
return (Settings)new XmlSerializer(typeof(Settings)).Deserialize(reader);
}
}
static void SaveAppSettings(Settings appSettings){
using (var writer = new XmlTextWriter(SettingsFilePath, Encoding.UTF8)) {
writer.Formatting = Formatting.Indented;
new XmlSerializer(typeof(Settings)).Serialize(writer, appSettings);
}
}
#endregion
#region Settings helper class
public class Settings
{
public int Setting1 { get; set; }
public bool Setting2 { get; set; }
public string Setting3 { get; set; }
}
#endregion
}
}
Usage pattern:
nt test = AppSettings.GetSetting1();
AppSettings.SetSetting1(100);
And now let's have a look at the object oriented version. Here the advantage is that new settings can be added at runtime. But you do not have strongly typed names and values. Must be careful with string identifiers. Must know data type saved earlier when getting a value.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
namespace Common
{
public static class AppSettings
{
public static readonly string SettingsFilePath = new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName + @"\AppSettings.xml";
#region Get setting
public static object Get(string key) {
var fiFile = new FileInfo(SettingsFilePath);
if (!File.Exists(SettingsFilePath) || fiFile.Length <= 3) { // UTF-8 preamble.
throw new KeyNotFoundException("Key not found: " + key);
}
using (var reader = new XmlTextReader(SettingsFilePath)) {
var settings = (List<Setting>)new XmlSerializer(typeof(List<Setting>)).Deserialize(reader);
var setting = settings.SingleOrDefault(s => s.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase));
if (setting != null) {
return setting.Value;
}
else {
throw new KeyNotFoundException("Key not found: " + key);
}
}
}
#endregion
#region Set setting
public static void Set(string key, object value) {
List<Setting> settings;
// Get list.
var fiFile = new FileInfo(SettingsFilePath);
if (!File.Exists(SettingsFilePath) || fiFile.Length <= 3) { // UTF-8 preamble.
settings = new List<Setting>();
}
else {
using (var reader = new XmlTextReader(SettingsFilePath)) {
settings = (List<Setting>)new XmlSerializer(typeof(List<Setting>)).Deserialize(reader);
}
}
// Update item.
var setting = settings.SingleOrDefault(s => s.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase));
if (setting != null) {
setting.Value = value;
}
else {
settings.Add(new Setting { Key = key, Value = value});
}
// Save list.
using (var writer = new XmlTextWriter(SettingsFilePath, Encoding.UTF8)) {
writer.Formatting = Formatting.Indented;
new XmlSerializer(typeof(List<Setting>)).Serialize(writer, settings);
}
}
#endregion
#region Setting helper class
public class Setting
{
public string Key;
public object Value;
}
#endregion
}
#region Exceptions
public class KeyNotFoundException : ApplicationException{
public KeyNotFoundException() {
}
public KeyNotFoundException(string message) : base(message){
}
}
#endregion
}
Usage pattern:
int test = (int)AppSettings.Get("setting1");
AppSettings.Set("setting1", 100);
Enjoy your choice!