Update: Configuration Optimisation and Caching

Loads entire configuration at start-up (rather than scope-based
loading). Deserialization occurs once, with the resulting value cached
and replayed if the requested type matches.
This commit is contained in:
Gary Sharp
2014-05-07 22:45:59 +10:00
parent 6b2cd47610
commit fb6067afc3
19 changed files with 455 additions and 282 deletions
+12
View File
@@ -18,4 +18,16 @@
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup> </startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration> </configuration>
@@ -1,9 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Data.Configuration namespace Disco.Data.Configuration
{ {
+39 -38
View File
@@ -1,11 +1,7 @@
using Disco.Data.Repository; using Disco.Data.Repository;
using Disco.Models.Repository;
using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
namespace Disco.Data.Configuration namespace Disco.Data.Configuration
{ {
@@ -15,29 +11,37 @@ namespace Disco.Data.Configuration
public abstract string Scope { get; } public abstract string Scope { get; }
/// <summary>
/// Creates a read-write configuration instance
/// </summary>
public ConfigurationBase(DiscoDataContext Database) public ConfigurationBase(DiscoDataContext Database)
{ {
this.Database = Database; this.Database = Database;
} }
protected List<ConfigurationItem> Items /// <summary>
/// Creates a read-only configuration instance
/// </summary>
public ConfigurationBase() : this(null) { }
protected IEnumerable<string> ItemKeys
{ {
get get
{ {
return ConfigurationCache.GetConfigurationItems(Database, this.Scope); return ConfigurationCache.GetScopeKeys(Database, this.Scope);
} }
} }
private void SetValue<ValueType>(string Key, ValueType Value) private void SetValue<T>(string Key, T Value)
{ {
ConfigurationCache.SetConfigurationValue(Database, this.Scope, Key, Value); ConfigurationCache.SetValue(Database, this.Scope, Key, Value);
} }
private ValueType GetValue<ValueType>(string Key, ValueType Default) private T GetValue<T>(string Key, T Default)
{ {
return ConfigurationCache.GetConfigurationValue(Database, this.Scope, Key, Default); return ConfigurationCache.GetValue(Database, this.Scope, Key, Default);
} }
protected void Set<ValueType>(ValueType Value, [CallerMemberName] string Key = null) protected void Set<T>(T Value, [CallerMemberName] string Key = null)
{ {
if (Key == null) if (Key == null)
throw new ArgumentNullException("Key"); throw new ArgumentNullException("Key");
@@ -45,7 +49,7 @@ namespace Disco.Data.Configuration
this.SetValue(Key, Value); this.SetValue(Key, Value);
} }
protected ValueType Get<ValueType>(ValueType Default, [CallerMemberName] string Key = null) protected T Get<T>(T Default, [CallerMemberName] string Key = null)
{ {
if (Key == null) if (Key == null)
throw new ArgumentNullException("Key"); throw new ArgumentNullException("Key");
@@ -55,7 +59,7 @@ namespace Disco.Data.Configuration
protected void SetObsfucated(string Value, [CallerMemberName] string Key = null) protected void SetObsfucated(string Value, [CallerMemberName] string Key = null)
{ {
this.Set(ConfigurationCache.ObsfucateValue(Value), Key); this.Set(Value.Obsfucate(), Key);
} }
protected string GetDeobsfucated(string Default, [CallerMemberName] string Key = null) protected string GetDeobsfucated(string Default, [CallerMemberName] string Key = null)
@@ -65,43 +69,40 @@ namespace Disco.Data.Configuration
if (obsfucatedValue == null) if (obsfucatedValue == null)
return Default; return Default;
else else
return ConfigurationCache.DeobsfucateValue(obsfucatedValue); return obsfucatedValue.Deobsfucate();
} }
protected void SetAsJson<ValueType>(ValueType Value, [CallerMemberName] string Key = null) protected void RemoveScope()
{ {
if (Value == null) ConfigurationCache.RemoveScope(Database, this.Scope);
this.Set<string>(null, Key);
else
this.Set(JsonConvert.SerializeObject(Value), Key);
} }
protected ValueType GetFromJson<ValueType>(ValueType Default, [CallerMemberName] string Key = null) protected void RemoveItem(string Key)
{ {
var jsonValue = this.Get<string>(null, Key); ConfigurationCache.RemoveScopeKey(Database, this.Scope, Key);
if (jsonValue == null)
return Default;
else
return JsonConvert.DeserializeObject<ValueType>(jsonValue);
} }
protected void SetAsEnum<EnumType>(EnumType Value, [CallerMemberName] string Key = null) [Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
protected void SetAsJson<T>(T Value, [CallerMemberName] string Key = null)
{ {
if (Value == null) this.Set(Value, Key);
this.Set<string>(null, Key);
else
this.Set(Value.ToString(), Key);
} }
protected EnumType GetFromEnum<EnumType>(EnumType Default, [CallerMemberName] string Key = null)
[Obsolete("Types are automatically serialized/deserialized if required, use Get<T>(T Default) instead.")]
protected T GetFromJson<T>(T Default, [CallerMemberName] string Key = null)
{ {
var stringValue = this.Get<string>(null, Key); return this.Get(Default, Key);
if (stringValue == null)
return Default;
else
return (EnumType)Enum.Parse(typeof(EnumType), stringValue);
} }
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
protected void SetAsEnum<T>(T Value, [CallerMemberName] string Key = null)
{
this.Set(Value, Key);
}
[Obsolete("Types are automatically serialized/deserialized if required, use Set<T>(T Value) instead.")]
protected T GetFromEnum<T>(T Default, [CallerMemberName] string Key = null)
{
return this.Get(Default, Key);
}
} }
} }
+321 -81
View File
@@ -1,143 +1,384 @@
using Disco.Data.Repository; using Disco.Data.Repository;
using Disco.Models.Repository; using Disco.Models.Repository;
using Newtonsoft.Json;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
namespace Disco.Data.Configuration namespace Disco.Data.Configuration
{ {
using ConfigurationCacheItemType = Tuple<ConfigurationItem, object>;
using ConfigurationCacheScopeType = ConcurrentDictionary<string, Tuple<ConfigurationItem, object>>;
using ConfigurationCacheType = ConcurrentDictionary<string, ConcurrentDictionary<string, Tuple<ConfigurationItem, object>>>;
internal static class ConfigurationCache internal static class ConfigurationCache
{ {
#region Cache #region Cache
private static Dictionary<String, Dictionary<String, ConfigurationItem>> configDictionary = new Dictionary<string, Dictionary<string, ConfigurationItem>>(); private static ConfigurationCacheType cacheStore = null;
private static List<ConfigurationItem> configurationItems = new List<ConfigurationItem>(); private static object configChangeLock = new object();
private static object configurationItemsLock = new object();
private static void LoadConfigurationItems(DiscoDataContext Database, string Scope, bool Reload) private static ConfigurationCacheType Cache(DiscoDataContext Database)
{ {
if (Reload || !configDictionary.ContainsKey(Scope)) if (ConfigurationCache.cacheStore == null)
{ {
lock (configurationItemsLock) lock (configChangeLock)
{ {
if (Reload || !configDictionary.ContainsKey(Scope)) if (ConfigurationCache.cacheStore == null)
{ {
if (Database == null) if (Database == null)
throw new InvalidOperationException("Cache-miss where Configuration Item requested from Cache-Only Configuration Context"); throw new InvalidOperationException("The Configuration must be loaded at least once before a Cache-Only Configuration Context is used");
var newItems = Database.ConfigurationItems.Where(ci => ci.Scope == Scope).ToArray(); var configurationItems = Database.ConfigurationItems.ToArray();
if (configDictionary.ContainsKey(Scope)) var indexedItems = configurationItems
{ .GroupBy(ci => ci.Scope)
var existingItems = configDictionary[Scope]; .Select(g =>
foreach (var existingItem in existingItems.Values) new KeyValuePair<string, ConfigurationCacheScopeType>(
{ g.Key,
configurationItems.Remove(existingItem); new ConfigurationCacheScopeType(
} g.Select(i => new KeyValuePair<string, ConfigurationCacheItemType>(i.Key, Tuple.Create(i, (object)null))))));
}
configurationItems.AddRange(newItems); cacheStore = new ConfigurationCacheType(indexedItems);
configDictionary[Scope] = newItems.ToDictionary(ci => ci.Key);
} }
} }
} }
return ConfigurationCache.cacheStore;
} }
private static Dictionary<string, Dictionary<string, ConfigurationItem>> ConfigurationDictionary(DiscoDataContext Database, string IncludingScope)
private static ConfigurationCacheItemType CacheGetItem(DiscoDataContext Database, string Scope, string Key)
{ {
LoadConfigurationItems(Database, IncludingScope, false); var cache = Cache(Database);
return configDictionary;
} ConfigurationCacheScopeType scopeCache;
private static ConfigurationItem ConfigurationItem(DiscoDataContext Database, string Scope, string Key) if (cache.TryGetValue(Scope, out scopeCache))
{
Dictionary<string, ConfigurationItem> scopeDict = default(Dictionary<string, ConfigurationItem>);
if (ConfigurationDictionary(Database, Scope).TryGetValue(Scope, out scopeDict))
{ {
ConfigurationItem item = default(ConfigurationItem); ConfigurationCacheItemType item = default(ConfigurationCacheItemType);
if (scopeDict.TryGetValue(Key, out item)) if (scopeCache.TryGetValue(Key, out item))
return item; return item;
} }
return null; return null;
} }
private static List<ConfigurationItem> ConfigurationItems(DiscoDataContext Database, string IncludingScope)
private static ConfigurationCacheItemType CacheSetItem(DiscoDataContext Database, string Scope, string Key, string Value, object ObjectValue)
{ {
LoadConfigurationItems(Database, IncludingScope, false); if (Database == null)
return configurationItems; throw new InvalidOperationException("Cannot save changes with a Cache-Only Configuration Context");
var item = CacheGetItem(Database, Scope, Key);
if (item == null && Value == null)
{
// No Change - already null
return null;
}
else if (item == null)
{
// New Configuration Item
lock (configChangeLock)
{
// Check again for thread safety
item = CacheGetItem(Database, Scope, Key);
if (item == null)
{
// Create Configuration Item
var configItem = new ConfigurationItem() { Scope = Scope, Key = Key, Value = Value };
item = new ConfigurationCacheItemType(configItem, ObjectValue);
// Add Item to DB
Database.ConfigurationItems.Add(configItem);
// Add Item to Cache
ConfigurationCacheScopeType scopeCache;
if (!cacheStore.TryGetValue(Scope, out scopeCache))
{
scopeCache = new ConfigurationCacheScopeType();
cacheStore.TryAdd(Scope, scopeCache);
}
scopeCache.TryAdd(Key, item);
return item;
}
}
}
if (item != null)
{
// Existing Configuration Item
lock (configChangeLock)
{
var configItem = item.Item1;
// Compare Values
if (item.Item1.Value == Value)
{
// No Change - Update Cache Reference Only
return SetItemTypeValue(item, ObjectValue);
}
else
{
var entityInfo = Database.Entry(configItem);
if (entityInfo.State == System.Data.EntityState.Detached)
{
// Reload Item from DB
configItem = Database.ConfigurationItems.Where(i => i.Scope == Scope && i.Key == Key).First();
}
if (Value == null)
{
// Remove item from Database
Database.ConfigurationItems.Remove(configItem);
// Remove item from Cache
ConfigurationCacheScopeType scopeCache;
if (cacheStore.TryGetValue(Scope, out scopeCache))
{
scopeCache.TryRemove(Key, out item);
}
return null;
}
else
{
// Update Database
configItem.Value = Value;
// Update Cache
ConfigurationCacheScopeType scopeCache;
if (cacheStore.TryGetValue(Scope, out scopeCache))
{
scopeCache.TryRemove(Key, out item);
item = new ConfigurationCacheItemType(configItem, ObjectValue);
scopeCache.TryAdd(Key, item);
return item;
}
}
}
}
}
return null;
}
private static ConfigurationCacheItemType SetItemTypeValue(ConfigurationCacheItemType ExistingItem, object Value)
{
var cache = ConfigurationCache.cacheStore;
ConfigurationCacheScopeType scopeCache;
if (cache.TryGetValue(ExistingItem.Item1.Scope, out scopeCache))
{
ConfigurationCacheItemType newItem = new ConfigurationCacheItemType(ExistingItem.Item1, Value);
scopeCache.TryUpdate(ExistingItem.Item1.Key, newItem, ExistingItem);
return newItem;
}
return null;
} }
#endregion #endregion
#region Public Helpers #region Helpers
internal static ValueType GetConfigurationValue<ValueType>(DiscoDataContext Database, string Scope, string Key, ValueType Default) private static bool IsConvertableFromString(Type t)
{ {
var ci = ConfigurationItem(Database, Scope, Key); if (t == typeof(Boolean) ||
if (ci == null) t == typeof(Char) ||
t == typeof(SByte) ||
t == typeof(Byte) ||
t == typeof(Int16) || t == typeof(UInt16) ||
t == typeof(Int32) || t == typeof(UInt32) ||
t == typeof(Int64) || t == typeof(UInt64) ||
t == typeof(Single) ||
t == typeof(Double) ||
t == typeof(Decimal) ||
t == typeof(DateTime) ||
t == typeof(String))
return true;
else
return false;
}
#endregion
#region Cache Getters/Setters
internal static T GetValue<T>(DiscoDataContext Database, string Scope, string Key, T Default)
{
var item = CacheGetItem(Database, Scope, Key);
if (item == null)
return Default; return Default;
else else
return (ValueType)Convert.ChangeType(ci.Value, typeof(ValueType));
}
internal static void SetConfigurationValue<ValueType>(DiscoDataContext Database, string Scope, string Key, ValueType Value)
{
if (Database == null)
throw new InvalidOperationException("Cannot save changes with a CacheOnly Context");
var ci = ConfigurationItem(Database, Scope, Key);
if (ci == null && Value != null)
{ {
lock (configurationItemsLock) if (item.Item2 != null && item.Item2.GetType() == typeof(T))
{ {
ci = ConfigurationItem(Database, Scope, Key); // Return Cached Item
if (ci == null) return (T)item.Item2;
{
// Create Configuration Item
ci = new ConfigurationItem() { Scope = Scope, Key = Key, Value = Value.ToString() };
// Add Item to DB & Internal Collections
Database.ConfigurationItems.Add(ci);
ConfigurationItems(Database, Scope).Add(ci);
ConfigurationDictionary(Database, Scope)[Scope].Add(Key, ci);
ci = null;
}
} }
} else
if (ci != null)
{
lock (configurationItemsLock)
{ {
var entityInfo = Database.Entry(ci); // Convert Serialized Item
if (entityInfo.State == System.Data.EntityState.Detached) Type itemType = typeof(T);
{ object itemValue;
// Reload Scope from DB
LoadConfigurationItems(Database, Scope, true);
ci = ConfigurationItem(Database, Scope, Key);
}
if (Value == null) if (itemType == typeof(string))
{ {
Database.ConfigurationItems.Remove(ci); // string
configurationItems.Remove(ci); itemValue = item.Item1.Value;
configDictionary[Scope].Remove(Key); }
else if (itemType == typeof(object))
{
// object
itemValue = item.Item1.Value;
}
else if (IsConvertableFromString(itemType))
{
// IConvertable
itemValue = Convert.ChangeType(item.Item1.Value, itemType);
}
else if (itemType.BaseType != null && itemType.BaseType == typeof(Enum))
{
// Enum
itemValue = Enum.Parse(typeof(T), item.Item1.Value);
} }
else else
{ {
ci.Value = Value.ToString(); // JSON Deserialize
itemValue = JsonConvert.DeserializeObject<T>(item.Item1.Value);
}
// Set Item in Cache
SetItemTypeValue(item, itemValue);
return (T)itemValue;
}
}
}
internal static void SetValue<T>(DiscoDataContext Database, string Scope, string Key, T Value)
{
Type valueType = typeof(T);
string stringValue;
if (Value == null)
{
stringValue = null;
}
else if (valueType == typeof(object))
{
throw new ArgumentException(string.Format("Cannot serialize the configuration item [{0}].[{1}] which defines a type of [System.Object]", Scope, Key), "Value");
}
else if (IsConvertableFromString(valueType))
{
// string or supports IConvertable
stringValue = Value.ToString();
}
else if (valueType.BaseType != null && valueType.BaseType == typeof(Enum))
{
// Enum
stringValue = Value.ToString();
}
else
{
// JSON Serialize
stringValue = JsonConvert.SerializeObject(Value);
}
CacheSetItem(Database, Scope, Key, stringValue, Value);
}
#endregion
#region Cache Helpers
internal static IEnumerable<string> GetScopeKeys(DiscoDataContext Database, string Scope)
{
var cache = Cache(Database);
ConfigurationCacheScopeType scopeCache;
if (cache.TryGetValue(Scope, out scopeCache))
{
return scopeCache.Keys.ToList();
}
else
{
return Enumerable.Empty<string>();
}
}
internal static void RemoveScope(DiscoDataContext Database, string Scope)
{
if (Database == null)
throw new InvalidOperationException("Cannot save changes with a Cache-Only Configuration Context");
lock (configChangeLock)
{
// Remove item from Database
var items = Database.ConfigurationItems.Where(i => i.Scope == Scope).ToList();
items.ForEach(i => Database.ConfigurationItems.Remove(i));
// Remove item from Cache
if (cacheStore != null)
{
ConfigurationCacheScopeType scopeCache;
cacheStore.TryRemove(Scope, out scopeCache);
}
}
}
internal static void RemoveScopeKey(DiscoDataContext Database, string Scope, string Key)
{
if (Database == null)
throw new InvalidOperationException("Cannot save changes with a Cache-Only Configuration Context");
lock (configChangeLock)
{
var cacheItem = CacheGetItem(Database, Scope, Key);
ConfigurationItem configItem = null;
// Remove item from Database
if (cacheItem != null)
{
configItem = cacheItem.Item1;
var entityInfo = Database.Entry(configItem);
if (entityInfo.State == System.Data.EntityState.Detached)
{
// Reload Item from DB
configItem = Database.ConfigurationItems.Where(i => i.Scope == Scope && i.Key == Key).FirstOrDefault();
}
}
if (configItem == null)
{
// Load Item from DB
configItem = Database.ConfigurationItems.Where(i => i.Scope == Scope && i.Key == Key).FirstOrDefault();
}
if (configItem != null)
{
Database.ConfigurationItems.Remove(configItem);
}
// Remove item from Cache
if (cacheItem != null)
{
ConfigurationCacheScopeType scopeCache;
if (cacheStore.TryGetValue(Scope, out scopeCache))
{
scopeCache.TryRemove(Key, out cacheItem);
} }
} }
} }
} }
internal static List<ConfigurationItem> GetConfigurationItems(DiscoDataContext Database, string Scope) #endregion
{
return ConfigurationDictionary(Database, Scope)[Scope].Values.ToList();
}
internal static string ObsfucateValue(string Value) #region Obsfucation Helpers
internal static string Obsfucate(this string Value)
{ {
if (string.IsNullOrEmpty(Value)) if (string.IsNullOrEmpty(Value))
return Value; return Value;
else else
return Convert.ToBase64String(Encoding.Unicode.GetBytes(Value)); return Convert.ToBase64String(Encoding.Unicode.GetBytes(Value));
} }
internal static string DeobsfucateValue(string ObsfucatedValue) internal static string Deobsfucate(this string ObsfucatedValue)
{ {
if (string.IsNullOrEmpty(ObsfucatedValue)) if (string.IsNullOrEmpty(ObsfucatedValue))
return ObsfucatedValue; return ObsfucatedValue;
@@ -145,6 +386,5 @@ namespace Disco.Data.Configuration
return Encoding.Unicode.GetString(Convert.FromBase64String(ObsfucatedValue)); return Encoding.Unicode.GetString(Convert.FromBase64String(ObsfucatedValue));
} }
#endregion #endregion
} }
} }
@@ -1,9 +1,5 @@
using Disco.Data.Repository; using Disco.Data.Repository;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Data.Configuration.Modules namespace Disco.Data.Configuration.Modules
{ {
@@ -20,18 +16,18 @@ namespace Disco.Data.Configuration.Modules
{ {
get get
{ {
return GetFromJson<Dictionary<string, List<string>>>(null); return Get<Dictionary<string, List<string>>>(null);
} }
set set
{ {
SetAsJson(value); Set(value);
} }
} }
public bool? SearchAllForestServers public bool? SearchAllForestServers
{ {
get { return GetFromJson<bool?>(null); } get { return Get<bool?>(null); }
set { SetAsJson(value); } set { Set(value); }
} }
} }
} }
@@ -1,8 +1,4 @@
using Disco.Data.Repository; using Disco.Data.Repository;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Disco.Data.Configuration.Modules namespace Disco.Data.Configuration.Modules
{ {
@@ -1,86 +0,0 @@
// Removed 2012-06-14 G# - Properties moved to DeviceProfile model & DB Migrated in DBv3.
//
//
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using Disco.Models.Repository;
//namespace Disco.Data.Configuration.Modules
//{
// public class DeviceProfileConfiguration : ConfigurationBase
// {
// private DeviceProfilesConfiguration deviceProfilesConfig;
// private DeviceProfile deviceProfile;
// public DeviceProfileConfiguration(ConfigurationContext Context, DeviceProfile DeviceProfile)
// : base(Context)
// {
// this.deviceProfilesConfig = Context.DeviceProfiles;
// this.deviceProfile = DeviceProfile;
// }
// public override string Scope
// {
// get
// {
// return string.Format("DeviceProfile:{0}", this.deviceProfile.Id);
// }
// }
// public string ComputerNameTemplate
// {
// get
// {
// return this.GetValue("ComputerNameTemplate", "DeviceProfile.ShortName + '-' + SerialNumber");
// }
// set
// {
// this.SetValue("ComputerNameTemplate", value);
// }
// }
// public enum DeviceProfileDistributionTypes : int
// {
// OneToMany = 0,
// OneToOne = 1
// }
// public DeviceProfileDistributionTypes DistributionType
// {
// get
// {
// return (DeviceProfileDistributionTypes)this.GetValue("DistributionType", (int)DeviceProfileDistributionTypes.OneToMany);
// }
// set
// {
// this.SetValue("DistributionType", (int)value);
// }
// }
// public string OrganisationalUnit
// {
// get
// {
// return this.GetValue<string>("OrganisationalUnit", null);
// }
// set
// {
// this.SetValue("OrganisationalUnit", value);
// }
// }
// public bool AllocateWirelessCertificate
// {
// get
// {
// return this.GetValue("AllocateWirelessCertificate", false);
// }
// set
// {
// this.SetValue("AllocateWirelessCertificate", value);
// }
// }
// }
//}
@@ -1,9 +1,5 @@
using System; using Disco.Data.Repository;
using System.Collections.Generic; using System;
using System.Linq;
using System.Text;
using Disco.Models.Repository;
using Disco.Data.Repository;
namespace Disco.Data.Configuration.Modules namespace Disco.Data.Configuration.Modules
{ {
@@ -1,10 +1,7 @@
using Disco.Data.Repository; using Disco.Data.Repository;
using Disco.Models.BI.Job;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Disco.Models.BI.Job;
namespace Disco.Data.Configuration.Modules namespace Disco.Data.Configuration.Modules
{ {
@@ -46,14 +43,14 @@ namespace Disco.Data.Configuration.Modules
public LocationModes LocationMode public LocationModes LocationMode
{ {
get { return GetFromEnum<LocationModes>(LocationModes.Unrestricted); } get { return Get<LocationModes>(LocationModes.Unrestricted); }
set { SetAsEnum(value); } set { Set(value); }
} }
public List<string> LocationList public List<string> LocationList
{ {
get { return GetFromJson<List<string>>(new List<string>()); } get { return Get<List<string>>(new List<string>()); }
set { SetAsJson(value); } set { Set(value); }
} }
} }
} }
@@ -1,38 +1,22 @@
using System; using Disco.Data.Repository;
using Disco.Models.BI.Config;
using Newtonsoft.Json;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using Disco.Models.BI.Config;
using Disco.Models.Repository;
using Newtonsoft.Json;
using Disco.Data.Repository;
namespace Disco.Data.Configuration.Modules namespace Disco.Data.Configuration.Modules
{ {
public class OrganisationAddressesConfiguration : ConfigurationBase public class OrganisationAddressesConfiguration : ConfigurationBase
{ {
private const string scope = "OrganisationAddresses";
public OrganisationAddressesConfiguration(DiscoDataContext Database) : base(Database) { } public OrganisationAddressesConfiguration(DiscoDataContext Database) : base(Database) { }
public override string Scope { get { return "OrganisationAddresses"; } } public override string Scope { get { return scope; } }
public OrganisationAddress GetAddress(int Id) public OrganisationAddress GetAddress(int Id)
{ {
var address = default(OrganisationAddress); return this.Get<OrganisationAddress>(null, Id.ToString());
var addressString = this.Get<string>(null, Id.ToString());
if (addressString != null)
{
if (addressString.StartsWith("{"))
{
// Assume Json
address = JsonConvert.DeserializeObject<OrganisationAddress>(addressString);
}
else
{
// Assume Old Storage Method
address = OrganisationAddress.FromConfigurationEntry(Id, addressString);
}
}
return address;
} }
public OrganisationAddress SetAddress(OrganisationAddress Address) public OrganisationAddress SetAddress(OrganisationAddress Address)
{ {
@@ -41,25 +25,21 @@ namespace Disco.Data.Configuration.Modules
Address.Id = NextOrganisationAddressId; Address.Id = NextOrganisationAddressId;
} }
string addressString = JsonConvert.SerializeObject(Address); this.Set(Address, Address.Id.ToString());
this.Set(addressString, Address.Id.ToString()); //Address.ToConfigurationEntry());
return Address; return Address;
} }
public void RemoveAddress(int Id) public void RemoveAddress(int Id)
{ {
// Set Config Item to null = Remove Configuration Item // Remove Configuration Item
this.Set<string>(null, Id.ToString()); this.RemoveItem(Id.ToString());
} }
public List<OrganisationAddress> Addresses public List<OrganisationAddress> Addresses
{ {
get get
{ {
return this.Items.Select(ca => ca.Value.StartsWith("{") ? return this.ItemKeys.Select(key => this.Get<OrganisationAddress>(null, key)).ToList();
JsonConvert.DeserializeObject<OrganisationAddress>(ca.Value) :
OrganisationAddress.FromConfigurationEntry(int.Parse(ca.Key), ca.Value)
).ToList();
} }
} }
@@ -78,5 +58,19 @@ namespace Disco.Data.Configuration.Modules
} }
} }
internal static void MigrateDatabase(DiscoDataContext Database)
{
// Migrate all organisation addresses to JSON
if (Database.ConfigurationItems.Count(i => i.Scope == scope && !i.Value.StartsWith("{")) > 0)
{
var items = Database.ConfigurationItems.Where(i => i.Scope == scope && !i.Value.StartsWith("{")).ToList();
items.ForEach(i =>
{
i.Value = JsonConvert.SerializeObject(OrganisationAddress.FromConfigurationEntry(int.Parse(i.Key), i.Value));
});
Database.SaveChanges();
}
}
} }
} }
@@ -1,13 +1,7 @@
using System; using Disco.Data.Repository;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Disco.Data.Repository;
using Disco.Models.Repository;
using System.IO;
using System.Security.Cryptography;
using Disco.Models.BI.Interop.Community; using Disco.Models.BI.Interop.Community;
using Newtonsoft.Json; using System;
using System.IO;
namespace Disco.Data.Configuration namespace Disco.Data.Configuration
{ {
@@ -270,11 +264,11 @@ namespace Disco.Data.Configuration
{ {
get get
{ {
return this.GetFromJson<UpdateResponse>(null); return this.Get<UpdateResponse>(null);
} }
set set
{ {
this.SetAsJson(value); this.Set(value);
} }
} }
public bool UpdateBetaDeployment public bool UpdateBetaDeployment
+1 -2
View File
@@ -77,7 +77,6 @@
<Compile Include="Configuration\Modules\JobPreferencesConfiguration.cs" /> <Compile Include="Configuration\Modules\JobPreferencesConfiguration.cs" />
<Compile Include="Configuration\SystemConfiguration.cs" /> <Compile Include="Configuration\SystemConfiguration.cs" />
<Compile Include="Configuration\Modules\BootstrapperConfiguration.cs" /> <Compile Include="Configuration\Modules\BootstrapperConfiguration.cs" />
<Compile Include="Configuration\Modules\DeviceProfileConfiguration.cs" />
<Compile Include="Configuration\Modules\DeviceProfilesConfiguration.cs" /> <Compile Include="Configuration\Modules\DeviceProfilesConfiguration.cs" />
<Compile Include="Configuration\Modules\OrganisationAddressesConfiguration.cs" /> <Compile Include="Configuration\Modules\OrganisationAddressesConfiguration.cs" />
<Compile Include="Migrations\201204250418485_DBv0.cs" /> <Compile Include="Migrations\201204250418485_DBv0.cs" />
@@ -203,7 +202,7 @@
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions> <ProjectExtensions>
<VisualStudio> <VisualStudio>
<UserProperties BuildVersion_BuildAction="Both" BuildVersion_UseGlobalSettings="False" BuildVersion_DetectChanges="False" BuildVersion_StartDate="2011/7/1" BuildVersion_BuildVersioningStyle="None.DeltaBaseYear.MonthAndDayStamp.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_UpdateFileVersion="True" /> <UserProperties BuildVersion_UpdateFileVersion="True" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.DeltaBaseYear.MonthAndDayStamp.TimeStamp" BuildVersion_StartDate="2011/7/1" BuildVersion_DetectChanges="False" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildAction="Both" />
</VisualStudio> </VisualStudio>
</ProjectExtensions> </ProjectExtensions>
<Import Project="$(SolutionDir)\.nuget\nuget.targets" /> <Import Project="$(SolutionDir)\.nuget\nuget.targets" />
+12
View File
@@ -19,7 +19,11 @@ namespace Disco.Data.Repository
Database.SeedJobTypes(); Database.SeedJobTypes();
Database.SeedJobSubTypes(); Database.SeedJobSubTypes();
Database.SaveChanges();
// Migration Maintenance // Migration Maintenance
Database.MigrateConfiguration();
Database.MigratePreDomainObjects(); Database.MigratePreDomainObjects();
} }
@@ -311,6 +315,14 @@ namespace Disco.Data.Repository
} }
// End Added: 2013-02-07 G# // End Added: 2013-02-07 G#
public static void MigrateConfiguration(this DiscoDataContext Database)
{
// Organisation Addresses - Force all to JSON serializing
Configuration.Modules.OrganisationAddressesConfiguration.MigrateDatabase(Database);
Database.SaveChanges();
}
#region Migrate Users SQL #region Migrate Users SQL
private const string MigratePreDomainUsers_Sql = @"INSERT INTO [Users] SELECT @IdNew, u.DisplayName, u.Surname, u.GivenName, u.PhoneNumber, u.EmailAddress FROM [Users] u WHERE [Id]=@IdExisting; private const string MigratePreDomainUsers_Sql = @"INSERT INTO [Users] SELECT @IdNew, u.DisplayName, u.Surname, u.GivenName, u.PhoneNumber, u.EmailAddress FROM [Users] u WHERE [Id]=@IdExisting;
+12
View File
@@ -14,4 +14,16 @@
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup> </startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration> </configuration>
+12
View File
@@ -14,4 +14,16 @@
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup> </startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration> </configuration>
@@ -31,7 +31,7 @@ namespace Disco.Web.Areas.Config.Controllers
if (m == null || m.DeviceProfile == null) if (m == null || m.DeviceProfile == null)
throw new ArgumentException("Invalid Device Profile Id", "id"); throw new ArgumentException("Invalid Device Profile Id", "id");
m.OrganisationAddresses = Database.DiscoConfiguration.OrganisationAddresses.Addresses; m.OrganisationAddresses = Database.DiscoConfiguration.OrganisationAddresses.Addresses.OrderBy(a => a.Name).ToList();
if (m.DeviceProfile.DefaultOrganisationAddress.HasValue) if (m.DeviceProfile.DefaultOrganisationAddress.HasValue)
m.DefaultOrganisationAddress = Database.DiscoConfiguration.OrganisationAddresses.GetAddress(m.DeviceProfile.DefaultOrganisationAddress.Value); m.DefaultOrganisationAddress = Database.DiscoConfiguration.OrganisationAddresses.GetAddress(m.DeviceProfile.DefaultOrganisationAddress.Value);
@@ -2,6 +2,7 @@
using Disco.Services.Authorization; using Disco.Services.Authorization;
using Disco.Services.Plugins.Features.UIExtension; using Disco.Services.Plugins.Features.UIExtension;
using Disco.Services.Web; using Disco.Services.Web;
using System.Linq;
using System.Web.Mvc; using System.Web.Mvc;
namespace Disco.Web.Areas.Config.Controllers namespace Disco.Web.Areas.Config.Controllers
@@ -18,7 +19,7 @@ namespace Disco.Web.Areas.Config.Controllers
viewModel.OrganisationName = Database.DiscoConfiguration.OrganisationName; viewModel.OrganisationName = Database.DiscoConfiguration.OrganisationName;
viewModel.MultiSiteMode = Database.DiscoConfiguration.MultiSiteMode; viewModel.MultiSiteMode = Database.DiscoConfiguration.MultiSiteMode;
viewModel.OrganisationAddresses = Database.DiscoConfiguration.OrganisationAddresses.Addresses; viewModel.OrganisationAddresses = Database.DiscoConfiguration.OrganisationAddresses.Addresses.OrderBy(a => a.Name).ToList();
// UI Extensions // UI Extensions
UIExtensions.ExecuteExtensions<ConfigOrganisationIndexModel>(this.ControllerContext, viewModel); UIExtensions.ExecuteExtensions<ConfigOrganisationIndexModel>(this.ControllerContext, viewModel);
+1 -1
View File
@@ -2053,7 +2053,7 @@
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile> <SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties> </WebProjectProperties>
</FlavorProperties> </FlavorProperties>
<UserProperties BuildVersion_UpdateFileVersion="True" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_BuildVersioningStyle="None.DeltaBaseYear.MonthAndDayStamp.TimeStamp" BuildVersion_DetectChanges="False" BuildVersion_UseGlobalSettings="False" BuildVersion_BuildAction="Both" BuildVersion_StartDate="2011/7/1" /> <UserProperties BuildVersion_StartDate="2011/7/1" BuildVersion_BuildAction="Both" BuildVersion_UseGlobalSettings="False" BuildVersion_DetectChanges="False" BuildVersion_BuildVersioningStyle="None.DeltaBaseYear.MonthAndDayStamp.TimeStamp" BuildVersion_UpdateAssemblyVersion="True" BuildVersion_UpdateFileVersion="True" />
</VisualStudio> </VisualStudio>
</ProjectExtensions> </ProjectExtensions>
<Import Project="$(SolutionDir)\.nuget\nuget.targets" /> <Import Project="$(SolutionDir)\.nuget\nuget.targets" />
+1 -1
View File
@@ -96,7 +96,7 @@ namespace Disco.Web.Models.Job
if (!string.IsNullOrEmpty(WarrantyProviderId)) if (!string.IsNullOrEmpty(WarrantyProviderId))
WarrantyProvider = Plugins.GetPluginFeature(WarrantyProviderId, typeof(WarrantyProviderFeature)); WarrantyProvider = Plugins.GetPluginFeature(WarrantyProviderId, typeof(WarrantyProviderFeature));
this.OrganisationAddresses = Database.DiscoConfiguration.OrganisationAddresses.Addresses; this.OrganisationAddresses = Database.DiscoConfiguration.OrganisationAddresses.Addresses.OrderBy(a => a.Name).ToList();
if (!IsPostBack && !this.OrganisationAddressId.HasValue) if (!IsPostBack && !this.OrganisationAddressId.HasValue)
{ {