Improved configuration management

This commit is contained in:
Robert McRackan 2019-12-17 10:09:33 -05:00
parent 6c757773f7
commit 4994684690
3 changed files with 90 additions and 77 deletions

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace FileManager
{
@ -10,78 +11,87 @@ namespace FileManager
{
public string Filepath { get; }
// forgiving -- doesn't drop settings. old entries will continue to be persisted even if not publicly visible
private Dictionary<string, string> settingsDic { get; }
public string this[string key]
{
get => settingsDic[key];
set
{
if (settingsDic.ContainsKey(key) && settingsDic[key] == value)
return;
settingsDic[key] = value;
// auto-save to file
save();
}
}
// optimize for strings. expectation is most settings will be strings and a rare exception will be something else
private Dictionary<string, string> stringCache { get; } = new Dictionary<string, string>();
private Dictionary<string, object> objectCache { get; } = new Dictionary<string, object>();
public PersistentDictionary(string filepath)
{
Filepath = filepath;
// not found. create blank file
if (!File.Exists(Filepath))
{
// will create any missing directories, incl subdirectories. if all already exist: no action
Directory.CreateDirectory(Path.GetDirectoryName(filepath));
File.WriteAllText(Filepath, "{}");
// give system time to create file before first use
System.Threading.Thread.Sleep(100);
}
settingsDic = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(Filepath));
}
public void EnsureEntries<T>()
{
var stringProperties =
GetPropertiesToPersist(typeof(T))
.Select(p => p.Name)
.ToList();
var keys = settingsDic.Keys.Cast<string>().ToList();
var missingKeys = stringProperties.Except(keys).ToList();
if (!missingKeys.Any())
if (File.Exists(Filepath))
return;
foreach (var key in missingKeys)
settingsDic.Add(key, null);
save();
// will create any missing directories, incl subdirectories. if all already exist: no action
Directory.CreateDirectory(Path.GetDirectoryName(filepath));
File.WriteAllText(Filepath, "{}");
System.Threading.Thread.Sleep(100);
}
public string GetString(string propertyName)
{
if (!stringCache.ContainsKey(propertyName))
{
var jObject = readFile();
stringCache[propertyName] = jObject.ContainsKey(propertyName) ? jObject[propertyName].Value<string>() : null;
}
return stringCache[propertyName];
}
public T Get<T>(string propertyName) where T : class
=> GetObject(propertyName) is T obj ? obj : default;
public object GetObject(string propertyName)
{
if (!objectCache.ContainsKey(propertyName))
{
var jObject = readFile();
objectCache[propertyName] = jObject.ContainsKey(propertyName) ? jObject[propertyName].Value<object>() : null;
}
return objectCache[propertyName];
}
private object locker { get; } = new object();
private void save()
public void Set(string propertyName, string newValue)
{
// only do this check in string cache, NOT object cache
if (stringCache[propertyName] == newValue)
return;
// set cache
stringCache[propertyName] = newValue;
// set in file
lock (locker)
File.WriteAllText(Filepath, JsonConvert.SerializeObject(settingsDic, Formatting.Indented));
{
var jObject = readFile();
jObject[propertyName] = newValue;
File.WriteAllText(Filepath, JsonConvert.SerializeObject(jObject, Formatting.Indented));
}
}
public static IEnumerable<System.Reflection.PropertyInfo> GetPropertiesToPersist(Type type)
=> type
.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
.Where(p =>
// string properties only
p.PropertyType == typeof(string)
// exclude indexer
&& p.GetIndexParameters().Length == 0
// exclude read-only, write-only
&& p.GetGetMethod(false) != null
&& p.GetSetMethod(false) != null
).ToList();
public void Set(string propertyName, object newValue)
{
// set cache
objectCache[propertyName] = newValue;
// set in file
lock (locker)
{
var jObject = readFile();
jObject[propertyName] = JToken.Parse(JsonConvert.SerializeObject(newValue));
File.WriteAllText(Filepath, JsonConvert.SerializeObject(jObject, Formatting.Indented));
}
}
private JObject readFile()
{
var settingsJsonContents = File.ReadAllText(Filepath);
var jObject = JsonConvert.DeserializeObject<JObject>(settingsJsonContents);
return jObject;
}
}
}