Browse Source

Added a database ID to Info()

Kenric Nugteren 1 year ago
parent
commit
c49c14a590

+ 2 - 1
InABox.Client.Local/LocalClient.cs

@@ -232,7 +232,8 @@ namespace InABox.Clients
             {
             {
                 Version = CoreUtils.GetVersion(),
                 Version = CoreUtils.GetVersion(),
                 ColorScheme = DbFactory.ColorScheme,
                 ColorScheme = DbFactory.ColorScheme,
-                Logo = DbFactory.Logo
+                Logo = DbFactory.Logo,
+                DatabaseID = DbFactory.ID
             };
             };
 
 
         }
         }

+ 2 - 0
InABox.Core/Client/ClientFactory.cs

@@ -36,6 +36,8 @@ namespace InABox.Clients
 
 
         public static Guid SessionID { get; set; }
         public static Guid SessionID { get; set; }
 
 
+        public static Guid DatabaseID { get; set; }
+
 
 
         public static Platform Platform { get; private set; }
         public static Platform Platform { get; private set; }
 
 

+ 6 - 1
InABox.Core/Client/Request.cs

@@ -877,13 +877,15 @@ namespace InABox.Clients
         public int RestPort { get; set; }
         public int RestPort { get; set; }
         public int RPCPort { get; set; }
         public int RPCPort { get; set; }
 
 
+        public Guid DatabaseID { get; set; }
+
         [JsonConstructor]
         [JsonConstructor]
         public DatabaseInfo()
         public DatabaseInfo()
         {
         {
             Version = "";
             Version = "";
         }
         }
 
 
-        public DatabaseInfo(string? colorScheme, byte[]? logo, string version, bool isHTTTPS, int restPort, int rpcPort)
+        public DatabaseInfo(string? colorScheme, byte[]? logo, string version, bool isHTTTPS, int restPort, int rpcPort, Guid databaseID)
         {
         {
             ColorScheme = colorScheme;
             ColorScheme = colorScheme;
             Logo = logo;
             Logo = logo;
@@ -891,6 +893,7 @@ namespace InABox.Clients
             IsHTTPS = isHTTTPS;
             IsHTTPS = isHTTTPS;
             RestPort = restPort;
             RestPort = restPort;
             RPCPort = rpcPort;
             RPCPort = rpcPort;
+            DatabaseID = databaseID;
         }
         }
 
 
         public void SerializeBinary(CoreBinaryWriter writer)
         public void SerializeBinary(CoreBinaryWriter writer)
@@ -914,6 +917,7 @@ namespace InABox.Clients
                 writer.Write(RestPort);
                 writer.Write(RestPort);
                 writer.Write(RPCPort);
                 writer.Write(RPCPort);
             }
             }
+            writer.Write(DatabaseID);
         }
         }
 
 
         public void DeserializeBinary(CoreBinaryReader reader)
         public void DeserializeBinary(CoreBinaryReader reader)
@@ -937,6 +941,7 @@ namespace InABox.Clients
                     Logger.Send(LogType.Error,"","Unable to read RestPort and RPCPort Values");
                     Logger.Send(LogType.Error,"","Unable to read RestPort and RPCPort Values");
                 }
                 }
             }
             }
+            DatabaseID = reader.ReadGuid();
         }
         }
     }
     }
     
     

+ 5 - 3
InABox.Core/DocumentCache.cs

@@ -363,7 +363,11 @@ namespace InABox.Core
 
 
         private string GetFolder()
         private string GetFolder()
         {
         {
-            return Path.Combine(CoreUtils.GetPath(), DocumentCaches.ServerName ?? "", "_documentcache", Tag);
+            return Path.Combine(
+                CoreUtils.GetPath(),
+                ClientFactory.DatabaseID.ToString(),
+                "_documentcache",
+                Tag);
         }
         }
 
 
         private string GetFileName(Guid documentID)
         private string GetFileName(Guid documentID)
@@ -378,8 +382,6 @@ namespace InABox.Core
     {
     {
         private static readonly Dictionary<Type, IDocumentCache> Caches = new Dictionary<Type, IDocumentCache>();
         private static readonly Dictionary<Type, IDocumentCache> Caches = new Dictionary<Type, IDocumentCache>();
 
 
-        public static string? ServerName { get; set; }
-
         #region Registry
         #region Registry
 
 
         public static void RegisterAll()
         public static void RegisterAll()

+ 194 - 196
InABox.Database/DataUpdater.cs

@@ -9,263 +9,261 @@ using System.Text;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
-namespace InABox.Database
+namespace InABox.Database;
+
+public class DatabaseVersion : BaseObject, IGlobalConfigurationSettings
 {
 {
+    public string Version { get; set; }
 
 
-    public class DatabaseVersion : BaseObject, IGlobalConfigurationSettings
+    public DatabaseVersion()
     {
     {
-        public string Version { get; set; }
-
-        public DatabaseVersion()
-        {
-            Version = "0.00";
-        }
+        Version = "0.00";
     }
     }
+}
 
 
-    public class VersionNumber
-    {
-        public int MajorVersion { get; set; }
-        public int MinorVersion { get; set; }
+public class VersionNumber
+{
+    public int MajorVersion { get; set; }
+    public int MinorVersion { get; set; }
 
 
-        public string Release { get; set; }
+    public string Release { get; set; }
 
 
-        public bool IsDevelopmentVersion { get; set; }
+    public bool IsDevelopmentVersion { get; set; }
 
 
-        public VersionNumber(int majorVersion, int minorVersion, string release = "", bool isDevelopmentVersion = false)
-        {
-            MajorVersion = majorVersion;
-            MinorVersion = minorVersion;
-            Release = release;
-            IsDevelopmentVersion = isDevelopmentVersion;
-        }
+    public VersionNumber(int majorVersion, int minorVersion, string release = "", bool isDevelopmentVersion = false)
+    {
+        MajorVersion = majorVersion;
+        MinorVersion = minorVersion;
+        Release = release;
+        IsDevelopmentVersion = isDevelopmentVersion;
+    }
 
 
-        private static Regex _format = new(@"^(\d+)\.(\d+)([a-zA-Z]*)$");
-        public static VersionNumber Parse(string versionStr)
+    private static Regex _format = new(@"^(\d+)\.(\d+)([a-zA-Z]*)$");
+    public static VersionNumber Parse(string versionStr)
+    {
+        if(versionStr == "???")
         {
         {
-            if(versionStr == "???")
-            {
-                return new(0, 0, "", true);
-            }
-            var match = _format.Match(versionStr);
-            if (!match.Success)
-                throw new FormatException($"'{versionStr}' is not a valid version!");
-            return new(int.Parse(match.Groups[1].Value), int.Parse(match.Groups[2].Value), match.Groups[3].Value, false);
+            return new(0, 0, "", true);
         }
         }
-        public static bool TryParse(string versionStr, [NotNullWhen(true)] out VersionNumber? version)
+        var match = _format.Match(versionStr);
+        if (!match.Success)
+            throw new FormatException($"'{versionStr}' is not a valid version!");
+        return new(int.Parse(match.Groups[1].Value), int.Parse(match.Groups[2].Value), match.Groups[3].Value, false);
+    }
+    public static bool TryParse(string versionStr, [NotNullWhen(true)] out VersionNumber? version)
+    {
+        if (versionStr == "???")
         {
         {
-            if (versionStr == "???")
-            {
-                version = new(0, 0, "", true);
-                return true;
-            }
-            var match = _format.Match(versionStr);
-            if (!match.Success)
-            {
-                version = null;
-                return false;
-            }
-            version = new(int.Parse(match.Groups[1].Value), int.Parse(match.Groups[2].Value), match.Groups[3].Value, false);
+            version = new(0, 0, "", true);
             return true;
             return true;
         }
         }
-
-        public static bool operator <(VersionNumber a, VersionNumber b)
+        var match = _format.Match(versionStr);
+        if (!match.Success)
         {
         {
-            if (a.IsDevelopmentVersion)
-            {
-                return false;
-            }
-            else if (b.IsDevelopmentVersion)
-            {
-                return true;
-            }
-            return a.MajorVersion < b.MajorVersion || 
-                (a.MajorVersion == b.MajorVersion &&
-                    (a.MinorVersion < b.MinorVersion || 
-                        (a.MinorVersion == b.MinorVersion && string.Compare(a.Release, b.Release, StringComparison.Ordinal) < 0)));
+            version = null;
+            return false;
         }
         }
-        public static bool operator >(VersionNumber a, VersionNumber b)
+        version = new(int.Parse(match.Groups[1].Value), int.Parse(match.Groups[2].Value), match.Groups[3].Value, false);
+        return true;
+    }
+
+    public static bool operator <(VersionNumber a, VersionNumber b)
+    {
+        if (a.IsDevelopmentVersion)
         {
         {
-            return b < a;
+            return false;
         }
         }
-
-        public static bool operator <=(VersionNumber a, VersionNumber b)
+        else if (b.IsDevelopmentVersion)
         {
         {
-            return !(b < a);
+            return true;
         }
         }
+        return a.MajorVersion < b.MajorVersion || 
+            (a.MajorVersion == b.MajorVersion &&
+                (a.MinorVersion < b.MinorVersion || 
+                    (a.MinorVersion == b.MinorVersion && string.Compare(a.Release, b.Release, StringComparison.Ordinal) < 0)));
+    }
+    public static bool operator >(VersionNumber a, VersionNumber b)
+    {
+        return b < a;
+    }
+
+    public static bool operator <=(VersionNumber a, VersionNumber b)
+    {
+        return !(b < a);
+    }
+
+    public static bool operator >=(VersionNumber a, VersionNumber b)
+    {
+        return !(a < b);
+    }
 
 
-        public static bool operator >=(VersionNumber a, VersionNumber b)
+    public override bool Equals(object? obj)
+    {
+        if(obj is VersionNumber v)
         {
         {
-            return !(a < b);
+            return this == v;
         }
         }
+        return false;
+    }
 
 
-        public override bool Equals(object? obj)
-        {
-            if(obj is VersionNumber v)
-            {
-                return this == v;
-            }
+    public override int GetHashCode()
+    {
+        if (IsDevelopmentVersion)
+            return 0;
+        return MajorVersion ^ MinorVersion ^ Release.GetHashCode();
+    }
+
+    public static bool operator ==(VersionNumber a, VersionNumber b)
+    {
+        if (a.IsDevelopmentVersion)
+            return b.IsDevelopmentVersion;
+        if (b.IsDevelopmentVersion)
             return false;
             return false;
-        }
+        return a.MajorVersion == b.MajorVersion && a.MinorVersion == b.MinorVersion && a.Release == b.Release;
+    }
+    public static bool operator !=(VersionNumber a, VersionNumber b)
+    {
+        if (a.IsDevelopmentVersion)
+            return !b.IsDevelopmentVersion;
+        if (b.IsDevelopmentVersion)
+            return true;
+        return a.MajorVersion != b.MajorVersion || a.MinorVersion != b.MinorVersion || a.Release != b.Release;
+    }
 
 
-        public override int GetHashCode()
-        {
-            if (IsDevelopmentVersion)
-                return 0;
-            return MajorVersion ^ MinorVersion ^ Release.GetHashCode();
-        }
+    public override string ToString()
+    {
+        return IsDevelopmentVersion ? "???" : $"{MajorVersion}.{MinorVersion:D2}{Release}";
+    }
+}
 
 
-        public static bool operator ==(VersionNumber a, VersionNumber b)
-        {
-            if (a.IsDevelopmentVersion)
-                return b.IsDevelopmentVersion;
-            if (b.IsDevelopmentVersion)
-                return false;
-            return a.MajorVersion == b.MajorVersion && a.MinorVersion == b.MinorVersion && a.Release == b.Release;
-        }
-        public static bool operator !=(VersionNumber a, VersionNumber b)
-        {
-            if (a.IsDevelopmentVersion)
-                return !b.IsDevelopmentVersion;
-            if (b.IsDevelopmentVersion)
-                return true;
-            return a.MajorVersion != b.MajorVersion || a.MinorVersion != b.MinorVersion || a.Release != b.Release;
-        }
+public static class DataUpdater
+{
+    private static Dictionary<VersionNumber, List<DatabaseUpdateScript>> updateScripts = new();
 
 
-        public override string ToString()
+    /// <summary>
+    /// Register a migration script to run when updating to this version.
+    /// 
+    /// <para>The <paramref name="action"/> should probably be repeatable; 
+    /// that is, if you run it a second time, it only updates data that needed updating. This way if it accidentally somehow gets run twice, there is no issue.
+    /// </para>
+    /// </summary>
+    /// <param name="version">The version to update to.</param>
+    /// <param name="action">The action to be run.</param>
+    public static void RegisterUpdateScript<TUpdater>()
+        where TUpdater : DatabaseUpdateScript, new()
+    {
+        var updater = new TUpdater();
+        
+        if(!updateScripts.TryGetValue(updater.Version, out var list))
         {
         {
-            return IsDevelopmentVersion ? "???" : $"{MajorVersion}.{MinorVersion:D2}{Release}";
+            list = new();
+            updateScripts[updater.Version] = list;
         }
         }
+        list.Add(updater);
     }
     }
 
 
-    public static class DataUpdater
+    private static bool MigrateDatabase(VersionNumber fromVersion, VersionNumber toVersion, out VersionNumber newVersion)
     {
     {
-        private static Dictionary<VersionNumber, List<DatabaseUpdateScript>> updateScripts = new();
+        var versionNumbers = updateScripts.Keys.ToList();
+        versionNumbers.Sort((x, y) => x == y ? 0 : x < y ? -1 : 1);
+
+        newVersion = fromVersion;
 
 
-        /// <summary>
-        /// Register a migration script to run when updating to this version.
-        /// 
-        /// <para>The <paramref name="action"/> should probably be repeatable; 
-        /// that is, if you run it a second time, it only updates data that needed updating. This way if it accidentally somehow gets run twice, there is no issue.
-        /// </para>
-        /// </summary>
-        /// <param name="version">The version to update to.</param>
-        /// <param name="action">The action to be run.</param>
-        public static void RegisterUpdateScript<TUpdater>()
-            where TUpdater : DatabaseUpdateScript, new()
+        int? index = null;
+        foreach (var (i, number) in versionNumbers.Select((x, i) => new Tuple<int, VersionNumber>(i, x)))
         {
         {
-            var updater = new TUpdater();
-            
-            if(!updateScripts.TryGetValue(updater.Version, out var list))
+            if (number > fromVersion)
             {
             {
-                list = new();
-                updateScripts[updater.Version] = list;
+                index = i;
+                break;
             }
             }
-            list.Add(updater);
         }
         }
-
-        private static bool MigrateDatabase(VersionNumber fromVersion, VersionNumber toVersion, out VersionNumber newVersion)
+        if(index != null && fromVersion < toVersion)
         {
         {
-            var versionNumbers = updateScripts.Keys.ToList();
-            versionNumbers.Sort((x, y) => x == y ? 0 : x < y ? -1 : 1);
-
-            newVersion = fromVersion;
-
-            int? index = null;
-            foreach (var (i, number) in versionNumbers.Select((x, i) => new Tuple<int, VersionNumber>(i, x)))
+            Logger.Send(LogType.Information, "", $"Updating database from {fromVersion} to {toVersion}");
+            for (int i = (int)index; i < versionNumbers.Count; i++)
             {
             {
-                if (number > fromVersion)
+                var version = versionNumbers[i];
+                if (toVersion < version)
                 {
                 {
-                    index = i;
                     break;
                     break;
                 }
                 }
-            }
-            if(index != null && fromVersion < toVersion)
-            {
-                Logger.Send(LogType.Information, "", $"Updating database from {fromVersion} to {toVersion}");
-                for (int i = (int)index; i < versionNumbers.Count; i++)
+                Logger.Send(LogType.Information, "", $"Executing update to {version}");
+                foreach(var updater in updateScripts[version])
                 {
                 {
-                    var version = versionNumbers[i];
-                    if (toVersion < version)
-                    {
-                        break;
-                    }
-                    Logger.Send(LogType.Information, "", $"Executing update to {version}");
-                    foreach(var updater in updateScripts[version])
+                    if (!updater.Update())
                     {
                     {
-                        if (!updater.Update())
-                        {
-                            Logger.Send(LogType.Error, "", $"Script failed, cancelling migration");
-                            return false;
-                        }
+                        Logger.Send(LogType.Error, "", $"Script failed, cancelling migration");
+                        return false;
                     }
                     }
-                    newVersion = version;
                 }
                 }
-                Logger.Send(LogType.Information, "", $"Data migration complete!");
+                newVersion = version;
             }
             }
-            newVersion = toVersion;
-            return true;
+            Logger.Send(LogType.Information, "", $"Data migration complete!");
         }
         }
+        newVersion = toVersion;
+        return true;
+    }
 
 
-        private static DatabaseVersion GetVersionSettings()
+    private static DatabaseVersion GetVersionSettings()
+    {
+        var result = DbFactory.Provider.Query(new Filter<GlobalSettings>(x => x.Section).IsEqualTo(nameof(DatabaseVersion)))
+            .Rows.FirstOrDefault()?.ToObject<GlobalSettings>();
+        if(result != null)
         {
         {
-            var result = DbFactory.Provider.Query(new Filter<GlobalSettings>(x => x.Section).IsEqualTo(nameof(DatabaseVersion)))
-                .Rows.FirstOrDefault()?.ToObject<GlobalSettings>();
-            if(result != null)
-            {
-                return Serialization.Deserialize<DatabaseVersion>(result.Contents);
-            }
-            var settings = new GlobalSettings() { Section = nameof(DatabaseVersion), Key = "" };
-            var dbVersion = new DatabaseVersion() { Version = "6.30b" };
-            settings.Contents = Serialization.Serialize(dbVersion);
-            DbFactory.Provider.Save(settings);
-            return dbVersion;
+            return Serialization.Deserialize<DatabaseVersion>(result.Contents);
         }
         }
+        var settings = new GlobalSettings() { Section = nameof(DatabaseVersion), Key = "" };
+        var dbVersion = new DatabaseVersion() { Version = "6.30b" };
+        settings.Contents = Serialization.Serialize(dbVersion);
+        DbFactory.Provider.Save(settings);
+        return dbVersion;
+    }
 
 
-        private static VersionNumber GetDatabaseVersion()
-        {
-            var dbVersion = GetVersionSettings();
-            return VersionNumber.Parse(dbVersion.Version);
-        }
+    private static VersionNumber GetDatabaseVersion()
+    {
+        var dbVersion = GetVersionSettings();
+        return VersionNumber.Parse(dbVersion.Version);
+    }
 
 
-        private static void UpdateVersionNumber(VersionNumber version)
+    private static void UpdateVersionNumber(VersionNumber version)
+    {
+        if (version.IsDevelopmentVersion)
         {
         {
-            if (version.IsDevelopmentVersion)
-            {
-                return;
-            }
-            var dbVersion = GetVersionSettings();
-            dbVersion.Version = version.ToString();
-
-            var result = DbFactory.Provider.Query(new Filter<GlobalSettings>(x => x.Section).IsEqualTo(nameof(DatabaseVersion)))
-                .Rows.FirstOrDefault()?.ToObject<GlobalSettings>() ?? new GlobalSettings() { Section = nameof(DatabaseVersion), Key = "" };
-            result.OriginalValues["Contents"] = result.Contents;
-            result.Contents = Serialization.Serialize(dbVersion);
-            DbFactory.Provider.Save(result);
+            return;
         }
         }
+        var dbVersion = GetVersionSettings();
+        dbVersion.Version = version.ToString();
+
+        var result = DbFactory.Provider.Query(new Filter<GlobalSettings>(x => x.Section).IsEqualTo(nameof(DatabaseVersion)))
+            .Rows.FirstOrDefault()?.ToObject<GlobalSettings>() ?? new GlobalSettings() { Section = nameof(DatabaseVersion), Key = "" };
+        result.OriginalValues["Contents"] = result.Contents;
+        result.Contents = Serialization.Serialize(dbVersion);
+        DbFactory.Provider.Save(result);
+    }
 
 
-        /// <summary>
-        /// Migrates the database to the current version.
-        /// </summary>
-        /// <returns><c>false</c> if the migration fails.</returns>
-        public static bool MigrateDatabase()
+    /// <summary>
+    /// Migrates the database to the current version.
+    /// </summary>
+    /// <returns><c>false</c> if the migration fails.</returns>
+    public static bool MigrateDatabase()
+    {
+        try
         {
         {
-            try
-            {
-                var from = GetDatabaseVersion();
-                var to = VersionNumber.Parse(CoreUtils.GetVersion());
+            var from = GetDatabaseVersion();
+            var to = VersionNumber.Parse(CoreUtils.GetVersion());
 
 
-                var success = MigrateDatabase(from, to, out var newVersion);
-                if (newVersion != from)
-                {
-                    UpdateVersionNumber(newVersion);
-                }
-                return success;
-            }
-            catch(Exception e)
+            var success = MigrateDatabase(from, to, out var newVersion);
+            if (newVersion != from)
             {
             {
-                Logger.Send(LogType.Error, "", $"Error while migrating database: {CoreUtils.FormatException(e)}");
-                return false;
+                UpdateVersionNumber(newVersion);
             }
             }
+            return success;
+        }
+        catch(Exception e)
+        {
+            Logger.Send(LogType.Error, "", $"Error while migrating database: {CoreUtils.FormatException(e)}");
+            return false;
         }
         }
     }
     }
 }
 }

+ 495 - 446
InABox.Database/DbFactory.cs

@@ -4,561 +4,610 @@ using InABox.Configuration;
 using InABox.Core;
 using InABox.Core;
 using InABox.Scripting;
 using InABox.Scripting;
 
 
-namespace InABox.Database
+namespace InABox.Database;
+
+public class DatabaseMetadata : BaseObject, IGlobalConfigurationSettings
 {
 {
-    public static class DbFactory
-    {
-        public static Dictionary<string, ScriptDocument> LoadedScripts = new();
-        
-        private static IProvider? _provider;
-        public static IProvider Provider
-        {
-            get => _provider ?? throw new Exception("Provider is not set");
-            set => _provider = value;
-        }
-        public static bool IsProviderSet => _provider is not null;
-        
-        public static string? ColorScheme { get; set; }
-        public static byte[]? Logo { get; set; }
-        
-        // See notes in Request.DatabaseInfo class
-        // Once RPC transport is stable, these settings need
-        // to be removed
-        public static int RestPort { get; set; }
-        public static int RPCPort { get; set; }
-        
-        //public static Type[] Entities { get { return entities; } set { SetEntityTypes(value); } }
-        public static IEnumerable<Type> Entities
-        {
-            get { return CoreUtils.Entities.Where(x => x.GetInterfaces().Contains(typeof(IPersistent))); }
-        }
+    public Guid DatabaseID { get; set; } = Guid.NewGuid();
+}
 
 
-        public static Type[] Stores
-        {
-            get => stores;
-            set => SetStoreTypes(value);
-        }
+public static class DbFactory
+{
+    public static Dictionary<string, ScriptDocument> LoadedScripts = new();
 
 
-        public static DateTime Expiry { get; set; }
+    private static DatabaseMetadata MetaData { get; set; }
 
 
-        public static void Start()
+    public static Guid ID
+    {
+        get => MetaData.DatabaseID;
+        set
         {
         {
-            CoreUtils.CheckLicensing();
-            
-            var status = ValidateSchema();
-
-            if (status.Equals(SchemaStatus.New))
-                try
-                {
-                    Provider.CreateSchema(ConsolidatedObjectModel().ToArray());
-                    SaveSchema();
-                }
-                catch (Exception err)
-                {
-                    throw new Exception(string.Format("Unable to Create Schema\n\n{0}", err.Message));
-                }
-            else if (status.Equals(SchemaStatus.Changed))
-                try
-                {
-                    Provider.UpgradeSchema(ConsolidatedObjectModel().ToArray());
-                    SaveSchema();
-                }
-                catch (Exception err)
-                {
-                    throw new Exception(string.Format("Unable to Update Schema\n\n{0}", err.Message));
-                }
+            MetaData.DatabaseID = value;
+            SaveMetadata();
+        }
+    }
+    
+    private static IProvider? _provider;
+    public static IProvider Provider
+    {
+        get => _provider ?? throw new Exception("Provider is not set");
+        set => _provider = value;
+    }
+    public static bool IsProviderSet => _provider is not null;
+    
+    public static string? ColorScheme { get; set; }
+    public static byte[]? Logo { get; set; }
+    
+    // See notes in Request.DatabaseInfo class
+    // Once RPC transport is stable, these settings need
+    // to be removed
+    public static int RestPort { get; set; }
+    public static int RPCPort { get; set; }
+    
+    //public static Type[] Entities { get { return entities; } set { SetEntityTypes(value); } }
+    public static IEnumerable<Type> Entities
+    {
+        get { return CoreUtils.Entities.Where(x => x.GetInterfaces().Contains(typeof(IPersistent))); }
+    }
 
 
-            // Start the provider
-            Provider.Types = ConsolidatedObjectModel();
+    public static Type[] Stores
+    {
+        get => stores;
+        set => SetStoreTypes(value);
+    }
 
 
-            Provider.OnLog += LogMessage;
+    public static DateTime Expiry { get; set; }
 
 
-            Provider.Start();
+    public static void Start()
+    {
+        CoreUtils.CheckLicensing();
+        
+        var status = ValidateSchema();
 
 
-            if (!DataUpdater.MigrateDatabase())
+        if (status.Equals(SchemaStatus.New))
+            try
+            {
+                Provider.CreateSchema(ConsolidatedObjectModel().ToArray());
+                SaveSchema();
+            }
+            catch (Exception err)
+            {
+                throw new Exception(string.Format("Unable to Create Schema\n\n{0}", err.Message));
+            }
+        else if (status.Equals(SchemaStatus.Changed))
+            try
+            {
+                Provider.UpgradeSchema(ConsolidatedObjectModel().ToArray());
+                SaveSchema();
+            }
+            catch (Exception err)
             {
             {
-                throw new Exception("Database migration failed. Aborting startup");
+                throw new Exception(string.Format("Unable to Update Schema\n\n{0}", err.Message));
             }
             }
 
 
+        // Start the provider
+        Provider.Types = ConsolidatedObjectModel();
 
 
-            //Load up your custom properties here!
-            // Can't use clients (b/c were inside the database layer already
-            // but we can simply access the store directly :-)
-            //CustomProperty[] props = FindStore<CustomProperty>("", "", "", "").Load(new Filter<CustomProperty>(x=>x.ID).IsNotEqualTo(Guid.Empty),null);
-            var props = Provider.Query<CustomProperty>().Rows.Select(x => x.ToObject<CustomProperty>()).ToArray();
-            DatabaseSchema.Load(props);
+        Provider.OnLog += LogMessage;
 
 
-            AssertLicense();
-            BeginLicenseCheckTimer();
+        Provider.Start();
 
 
-            InitStores();
+        CheckMetadata();
 
 
-            LoadScripts();
+        if (!DataUpdater.MigrateDatabase())
+        {
+            throw new Exception("Database migration failed. Aborting startup");
         }
         }
 
 
-        #region License
 
 
-        private enum LicenseValidation
-        {
-            Valid,
-            Missing,
-            Expired,
-            Corrupt,
-            Tampered
-        }
+        //Load up your custom properties here!
+        // Can't use clients (b/c were inside the database layer already
+        // but we can simply access the store directly :-)
+        //CustomProperty[] props = FindStore<CustomProperty>("", "", "", "").Load(new Filter<CustomProperty>(x=>x.ID).IsNotEqualTo(Guid.Empty),null);
+        var props = Provider.Query<CustomProperty>().Rows.Select(x => x.ToObject<CustomProperty>()).ToArray();
+        DatabaseSchema.Load(props);
 
 
-        private static LicenseValidation CheckLicenseValidity(out License? license, out LicenseData? licenseData)
-        {
-            license = Provider.Load<License>().FirstOrDefault();
-            if (license is null)
-            {
-                licenseData = null;
-                return LicenseValidation.Missing;
-            }
+        AssertLicense();
+        BeginLicenseCheckTimer();
 
 
-            if (!LicenseUtils.TryDecryptLicense(license.Data, out licenseData, out var error))
-                return LicenseValidation.Corrupt;
+        InitStores();
 
 
-            if (licenseData.Expiry < DateTime.Now)
-                return LicenseValidation.Expired;
+        LoadScripts();
+    }
 
 
-            var userTrackingItems = Provider.Query(
-                new Filter<UserTracking>(x => x.ID).InList(licenseData.UserTrackingItems),
-                new Columns<UserTracking>(x => x.ID), log: false).Rows.Select(x => x.Get<UserTracking, Guid>(x => x.ID));
+    #region MetaData
 
 
-            foreach(var item in licenseData.UserTrackingItems)
-            {
-                if (!userTrackingItems.Contains(item))
-                {
-                    return LicenseValidation.Tampered;
-                }
-            }
-            return LicenseValidation.Valid;
+    private static void SaveMetadata()
+    {
+        var settings = new GlobalSettings
+        {
+            Section = nameof(DatabaseMetadata),
+            Key = "",
+            Contents = Serialization.Serialize(MetaData)
+        };
+        DbFactory.Provider.Save(settings);
+    }
+
+    private static void CheckMetadata()
+    {
+        var result = DbFactory.Provider.Query(new Filter<GlobalSettings>(x => x.Section).IsEqualTo(nameof(DatabaseMetadata)))
+            .Rows.FirstOrDefault()?.ToObject<GlobalSettings>();
+        var data = result is not null ? Serialization.Deserialize<DatabaseMetadata>(result.Contents) : null;
+        if (data is null)
+        {
+            MetaData = new DatabaseMetadata();
+            SaveMetadata();
+        }
+        else
+        {
+            MetaData = data;
         }
         }
+    }
 
 
-        private static int _expiredLicenseCounter = 0;
-        private static TimeSpan LicenseCheckInterval = TimeSpan.FromMinutes(10);
+    #endregion
 
 
-        private static bool _readOnly;
-        public static bool IsReadOnly { get => _readOnly; }
+    #region License
 
 
-        private static System.Timers.Timer LicenseTimer = new System.Timers.Timer(LicenseCheckInterval.TotalMilliseconds) { AutoReset = true };
+    private enum LicenseValidation
+    {
+        Valid,
+        Missing,
+        Expired,
+        Corrupt,
+        Tampered
+    }
 
 
-        private static void LogRenew(string message)
+    private static LicenseValidation CheckLicenseValidity(out License? license, out LicenseData? licenseData)
+    {
+        license = Provider.Load<License>().FirstOrDefault();
+        if (license is null)
         {
         {
-            LogImportant($"{message} Please renew your license before then, or your database will go into read-only mode; it will be locked for saving anything until you renew your license. For help with renewing your license, please see the documentation at https://prsdigital.com.au/wiki/index.php/License_Renewal.");
+            licenseData = null;
+            return LicenseValidation.Missing;
         }
         }
-        private static void LogLicenseExpiry(DateTime expiry)
+
+        if (!LicenseUtils.TryDecryptLicense(license.Data, out licenseData, out var error))
+            return LicenseValidation.Corrupt;
+
+        if (licenseData.Expiry < DateTime.Now)
+            return LicenseValidation.Expired;
+
+        var userTrackingItems = Provider.Query(
+            new Filter<UserTracking>(x => x.ID).InList(licenseData.UserTrackingItems),
+            new Columns<UserTracking>(x => x.ID), log: false).Rows.Select(x => x.Get<UserTracking, Guid>(x => x.ID));
+
+        foreach(var item in licenseData.UserTrackingItems)
         {
         {
-            if (expiry.Date == DateTime.Today)
-            {
-                LogRenew($"Your database license is expiring today at {expiry.TimeOfDay:HH:mm}!");
-                return;
-            }
-            var diffInDays = (expiry - DateTime.Now).TotalDays;
-            if(diffInDays < 1)
-            {
-                LogRenew($"Your database license will expire in less than a day, on the {expiry:dd MMM yyyy} at {expiry:hh:mm:tt}.");
-            }
-            else if(diffInDays < 3 && (_expiredLicenseCounter * LicenseCheckInterval).TotalHours >= 1)
+            if (!userTrackingItems.Contains(item))
             {
             {
-                LogRenew($"Your database license will expire in less than three days, on the {expiry:dd MMM yyyy} at {expiry:hh:mm:tt}.");
-                _expiredLicenseCounter = 0;
+                return LicenseValidation.Tampered;
             }
             }
-            else if(diffInDays < 7 && (_expiredLicenseCounter * LicenseCheckInterval).TotalHours >= 2)
-            {
-                LogRenew($"Your database license will expire in less than a week, on the {expiry:dd MMM yyyy} at {expiry:hh:mm:tt}.");
-                _expiredLicenseCounter = 0;
-            }
-            ++_expiredLicenseCounter;
         }
         }
+        return LicenseValidation.Valid;
+    }
 
 
-        public static void LogReadOnly()
-        {
-            LogError("Database is read-only because your license is invalid!");
-        }
+    private static int _expiredLicenseCounter = 0;
+    private static TimeSpan LicenseCheckInterval = TimeSpan.FromMinutes(10);
+
+    private static bool _readOnly;
+    public static bool IsReadOnly { get => _readOnly; }
 
 
-        private static void BeginReadOnly()
+    private static System.Timers.Timer LicenseTimer = new System.Timers.Timer(LicenseCheckInterval.TotalMilliseconds) { AutoReset = true };
+
+    private static void LogRenew(string message)
+    {
+        LogImportant($"{message} Please renew your license before then, or your database will go into read-only mode; it will be locked for saving anything until you renew your license. For help with renewing your license, please see the documentation at https://prsdigital.com.au/wiki/index.php/License_Renewal.");
+    }
+    private static void LogLicenseExpiry(DateTime expiry)
+    {
+        if (expiry.Date == DateTime.Today)
         {
         {
-            LogImportant("Your database is now in read-only mode, since your license is invalid; you will be unable to save any records to the database until you renew your license. For help with renewing your license, please see the documentation at https://prsdigital.com.au/wiki/index.php/License_Renewal.");
-            _readOnly = true;
+            LogRenew($"Your database license is expiring today at {expiry.TimeOfDay:HH:mm}!");
+            return;
         }
         }
-        private static void EndReadOnly()
+        var diffInDays = (expiry - DateTime.Now).TotalDays;
+        if(diffInDays < 1)
         {
         {
-            LogImportant("Valid license found; the database is no longer read-only.");
-            _readOnly = false;
+            LogRenew($"Your database license will expire in less than a day, on the {expiry:dd MMM yyyy} at {expiry:hh:mm:tt}.");
         }
         }
-
-        private static void BeginLicenseCheckTimer()
+        else if(diffInDays < 3 && (_expiredLicenseCounter * LicenseCheckInterval).TotalHours >= 1)
         {
         {
-            LicenseTimer.Elapsed += LicenseTimer_Elapsed;
-            LicenseTimer.Start();
+            LogRenew($"Your database license will expire in less than three days, on the {expiry:dd MMM yyyy} at {expiry:hh:mm:tt}.");
+            _expiredLicenseCounter = 0;
         }
         }
-
-        private static void LicenseTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
+        else if(diffInDays < 7 && (_expiredLicenseCounter * LicenseCheckInterval).TotalHours >= 2)
         {
         {
-            AssertLicense();
+            LogRenew($"Your database license will expire in less than a week, on the {expiry:dd MMM yyyy} at {expiry:hh:mm:tt}.");
+            _expiredLicenseCounter = 0;
         }
         }
+        ++_expiredLicenseCounter;
+    }
+
+    public static void LogReadOnly()
+    {
+        LogError("Database is read-only because your license is invalid!");
+    }
+
+    private static void BeginReadOnly()
+    {
+        LogImportant("Your database is now in read-only mode, since your license is invalid; you will be unable to save any records to the database until you renew your license. For help with renewing your license, please see the documentation at https://prsdigital.com.au/wiki/index.php/License_Renewal.");
+        _readOnly = true;
+    }
+    private static void EndReadOnly()
+    {
+        LogImportant("Valid license found; the database is no longer read-only.");
+        _readOnly = false;
+    }
+
+    private static void BeginLicenseCheckTimer()
+    {
+        LicenseTimer.Elapsed += LicenseTimer_Elapsed;
+        LicenseTimer.Start();
+    }
 
 
-        private static Random LicenseIDGenerate = new Random();
-        private static void UpdateValidLicense(License license, LicenseData licenseData)
+    private static void LicenseTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
+    {
+        AssertLicense();
+    }
+
+    private static Random LicenseIDGenerate = new Random();
+    private static void UpdateValidLicense(License license, LicenseData licenseData)
+    {
+        var ids = Provider.Query(
+            new Filter<UserTracking>(x => x.Created).IsGreaterThanOrEqualTo(licenseData.LastRenewal),
+            new Columns<UserTracking>(x => x.ID), log: false);
+        var newIDList = new List<Guid>();
+        if(ids.Rows.Count > 0)
         {
         {
-            var ids = Provider.Query(
-                new Filter<UserTracking>(x => x.Created).IsGreaterThanOrEqualTo(licenseData.LastRenewal),
-                new Columns<UserTracking>(x => x.ID), log: false);
-            var newIDList = new List<Guid>();
-            if(ids.Rows.Count > 0)
+            for (int i = 0; i < 10; i++)
             {
             {
-                for (int i = 0; i < 10; i++)
-                {
-                    newIDList.Add(ids.Rows[LicenseIDGenerate.Next(0, ids.Rows.Count)].Get<UserTracking, Guid>(x => x.ID));
-                }
+                newIDList.Add(ids.Rows[LicenseIDGenerate.Next(0, ids.Rows.Count)].Get<UserTracking, Guid>(x => x.ID));
             }
             }
-            licenseData.UserTrackingItems = newIDList.ToArray();
+        }
+        licenseData.UserTrackingItems = newIDList.ToArray();
 
 
-            if(LicenseUtils.TryEncryptLicense(licenseData, out var newData, out var error))
-            {
-                license.Data = newData;
-                Provider.Save(license);
-            }
+        if(LicenseUtils.TryEncryptLicense(licenseData, out var newData, out var error))
+        {
+            license.Data = newData;
+            Provider.Save(license);
         }
         }
+    }
 
 
-        private static void AssertLicense()
+    private static void AssertLicense()
+    {
+        var result = CheckLicenseValidity(out var license, out var licenseData);
+        if (IsReadOnly)
         {
         {
-            var result = CheckLicenseValidity(out var license, out var licenseData);
-            if (IsReadOnly)
+            if(result == LicenseValidation.Valid)
             {
             {
-                if(result == LicenseValidation.Valid)
-                {
-                    EndReadOnly();
-                }
-                return;
+                EndReadOnly();
             }
             }
+            return;
+        }
 
 
-            // TODO: Switch to real system
-            if(result != LicenseValidation.Valid)
+        // TODO: Switch to real system
+        if(result != LicenseValidation.Valid)
+        {
+            var newLicense = LicenseUtils.GenerateNewLicense();
+            if (LicenseUtils.TryEncryptLicense(newLicense, out var newData, out var error))
             {
             {
-                var newLicense = LicenseUtils.GenerateNewLicense();
-                if (LicenseUtils.TryEncryptLicense(newLicense, out var newData, out var error))
-                {
-                    if (license == null)
-                        license = new License();
-                    license.Data = newData;
-                    Provider.Save(license);
-                }
-                else
-                {
-                    Logger.Send(LogType.Error, "", $"Error updating license: {error}");
-                }
-                return;
+                if (license == null)
+                    license = new License();
+                license.Data = newData;
+                Provider.Save(license);
             }
             }
             else
             else
             {
             {
-                return;
-            }
-
-            switch (result)
-            {
-                case LicenseValidation.Valid:
-                    LogLicenseExpiry(licenseData!.Expiry);
-                    UpdateValidLicense(license, licenseData);
-                    break;
-                case LicenseValidation.Missing:
-                    LogImportant("Database is unlicensed!");
-                    BeginReadOnly();
-                    break;
-                case LicenseValidation.Expired:
-                    LogImportant("Database license has expired!");
-                    BeginReadOnly();
-                    break;
-                case LicenseValidation.Corrupt:
-                    LogImportant("Database license is corrupt - you will need to renew your license.");
-                    BeginReadOnly();
-                    break;
-                case LicenseValidation.Tampered:
-                    LogImportant("Database license has been tampered with - you will need to renew your license.");
-                    BeginReadOnly();
-                    break;
+                Logger.Send(LogType.Error, "", $"Error updating license: {error}");
             }
             }
+            return;
+        }
+        else
+        {
+            return;
+        }
+
+        switch (result)
+        {
+            case LicenseValidation.Valid:
+                LogLicenseExpiry(licenseData!.Expiry);
+                UpdateValidLicense(license, licenseData);
+                break;
+            case LicenseValidation.Missing:
+                LogImportant("Database is unlicensed!");
+                BeginReadOnly();
+                break;
+            case LicenseValidation.Expired:
+                LogImportant("Database license has expired!");
+                BeginReadOnly();
+                break;
+            case LicenseValidation.Corrupt:
+                LogImportant("Database license is corrupt - you will need to renew your license.");
+                BeginReadOnly();
+                break;
+            case LicenseValidation.Tampered:
+                LogImportant("Database license has been tampered with - you will need to renew your license.");
+                BeginReadOnly();
+                break;
         }
         }
+    }
 
 
-        #endregion
+    #endregion
 
 
-        #region Logging
+    #region Logging
 
 
-        private static void LogMessage(LogType type, string message)
-        {
-            Logger.Send(type, "", message);
-        }
+    private static void LogMessage(LogType type, string message)
+    {
+        Logger.Send(type, "", message);
+    }
 
 
-        private static void LogInfo(string message)
-        {
-            Logger.Send(LogType.Information, "", message);
-        }
-        private static void LogImportant(string message)
-        {
-            Logger.Send(LogType.Important, "", message);
-        }
-        private static void LogError(string message)
-        {
-            Logger.Send(LogType.Error, "", message);
-        }
+    private static void LogInfo(string message)
+    {
+        Logger.Send(LogType.Information, "", message);
+    }
+    private static void LogImportant(string message)
+    {
+        Logger.Send(LogType.Important, "", message);
+    }
+    private static void LogError(string message)
+    {
+        Logger.Send(LogType.Error, "", message);
+    }
 
 
-        #endregion
+    #endregion
 
 
-        public static void InitStores()
+    public static void InitStores()
+    {
+        foreach (var storetype in stores)
         {
         {
-            foreach (var storetype in stores)
-            {
-                var store = (Activator.CreateInstance(storetype) as IStore)!;
-                store.Provider = Provider;
-                store.Init();
-            }
+            var store = (Activator.CreateInstance(storetype) as IStore)!;
+            store.Provider = Provider;
+            store.Init();
         }
         }
+    }
 
 
-        public static IStore FindStore(Type type, Guid userguid, string userid, Platform platform, string version)
-        {
-            var defType = typeof(Store<>).MakeGenericType(type);
-            Type? subType = Stores.Where(myType => myType.IsSubclassOf(defType)).FirstOrDefault();
-
-            var store = (Activator.CreateInstance(subType ?? defType) as IStore)!;
-            
-            store.Provider = Provider;
-            store.UserGuid = userguid;
-            store.UserID = userid;
-            store.Platform = platform;
-            store.Version = version;
+    public static IStore FindStore(Type type, Guid userguid, string userid, Platform platform, string version)
+    {
+        var defType = typeof(Store<>).MakeGenericType(type);
+        Type? subType = Stores.Where(myType => myType.IsSubclassOf(defType)).FirstOrDefault();
 
 
-            return store;
-        }
+        var store = (Activator.CreateInstance(subType ?? defType) as IStore)!;
         
         
-        public static IStore<TEntity> FindStore<TEntity>(Guid userguid, string userid, Platform platform, string version) 
-            where TEntity : Entity, new()
-        {
-            return (FindStore(typeof(TEntity), userguid, userid, platform, version) as IStore<TEntity>)!;
-        }
+        store.Provider = Provider;
+        store.UserGuid = userguid;
+        store.UserID = userid;
+        store.Platform = platform;
+        store.Version = version;
 
 
-        private static CoreTable DoQueryMultipleQuery<TEntity>(
-            IQueryDef query,
-            Guid userguid, string userid, Platform platform, string version) 
-            where TEntity : Entity, new()
-        {
-            var store = FindStore<TEntity>(userguid, userid, platform, version);
-            return store.Query(query.Filter as Filter<TEntity>, query.Columns as Columns<TEntity>, query.SortOrder as SortOrder<TEntity>);
-        }
+        return store;
+    }
+    
+    public static IStore<TEntity> FindStore<TEntity>(Guid userguid, string userid, Platform platform, string version) 
+        where TEntity : Entity, new()
+    {
+        return (FindStore(typeof(TEntity), userguid, userid, platform, version) as IStore<TEntity>)!;
+    }
 
 
-        public static Dictionary<string, CoreTable> QueryMultiple(
-            Dictionary<string, IQueryDef> queries, 
-            Guid userguid, string userid, Platform platform, string version)
-        {
-            var result = new Dictionary<string, CoreTable>();
+    private static CoreTable DoQueryMultipleQuery<TEntity>(
+        IQueryDef query,
+        Guid userguid, string userid, Platform platform, string version) 
+        where TEntity : Entity, new()
+    {
+        var store = FindStore<TEntity>(userguid, userid, platform, version);
+        return store.Query(query.Filter as Filter<TEntity>, query.Columns as Columns<TEntity>, query.SortOrder as SortOrder<TEntity>);
+    }
 
 
-            var queryMethod = typeof(DbFactory).GetMethod(nameof(DoQueryMultipleQuery), BindingFlags.NonPublic | BindingFlags.Static)!;
+    public static Dictionary<string, CoreTable> QueryMultiple(
+        Dictionary<string, IQueryDef> queries, 
+        Guid userguid, string userid, Platform platform, string version)
+    {
+        var result = new Dictionary<string, CoreTable>();
+
+        var queryMethod = typeof(DbFactory).GetMethod(nameof(DoQueryMultipleQuery), BindingFlags.NonPublic | BindingFlags.Static)!;
 
 
-            var tasks = new List<Task>();
-            foreach (var item in queries)
-                tasks.Add(Task.Run(() =>
+        var tasks = new List<Task>();
+        foreach (var item in queries)
+            tasks.Add(Task.Run(() =>
+            {
+                result[item.Key] = (queryMethod.MakeGenericMethod(item.Value.Type).Invoke(Provider, new object[]
                 {
                 {
-                    result[item.Key] = (queryMethod.MakeGenericMethod(item.Value.Type).Invoke(Provider, new object[]
-                    {
-                        item.Value,
-                        userguid, userid, platform, version
-                    }) as CoreTable)!;
-                }));
-
-            Task.WaitAll(tasks.ToArray());
-            return result;
-        }
+                    item.Value,
+                    userguid, userid, platform, version
+                }) as CoreTable)!;
+            }));
 
 
-        #region Supported Types
+        Task.WaitAll(tasks.ToArray());
+        return result;
+    }
 
 
-        private class ModuleConfiguration : Dictionary<string, bool>, ILocalConfigurationSettings
-        {
-        }
+    #region Supported Types
 
 
-        private static Type[]? _dbtypes;
+    private class ModuleConfiguration : Dictionary<string, bool>, ILocalConfigurationSettings
+    {
+    }
 
 
-        public static IEnumerable<string> SupportedTypes()
-        {
-            _dbtypes ??= LoadSupportedTypes();
-            return _dbtypes.Select(x => x.EntityName().Replace(".", "_"));
-        }
+    private static Type[]? _dbtypes;
 
 
-        private static Type[] LoadSupportedTypes()
-        {
-            var result = new List<Type>();
+    public static IEnumerable<string> SupportedTypes()
+    {
+        _dbtypes ??= LoadSupportedTypes();
+        return _dbtypes.Select(x => x.EntityName().Replace(".", "_"));
+    }
 
 
-            var path = Provider.URL.ToLower();
-            var config = new LocalConfiguration<ModuleConfiguration>(Path.GetDirectoryName(path) ?? "", Path.GetFileName(path)).Load();
-            var bChanged = false;
-            foreach (var type in Entities)
-            {
-                var key = type.EntityName();
-                if (config.ContainsKey(key))
-                {
-                    if (config[key])
-                        //Logger.Send(LogType.Information, "", String.Format("{0} is enabled", key));
-                        result.Add(type);
-                    else
-                        Logger.Send(LogType.Information, "", string.Format("Entity [{0}] is disabled", key));
-                }
-                else
-                {
-                    //Logger.Send(LogType.Information, "", String.Format("{0} does not exist - enabling", key));
+    private static Type[] LoadSupportedTypes()
+    {
+        var result = new List<Type>();
 
 
-                    config[key] = true;
+        var path = Provider.URL.ToLower();
+        var config = new LocalConfiguration<ModuleConfiguration>(Path.GetDirectoryName(path) ?? "", Path.GetFileName(path)).Load();
+        var bChanged = false;
+        foreach (var type in Entities)
+        {
+            var key = type.EntityName();
+            if (config.ContainsKey(key))
+            {
+                if (config[key])
+                    //Logger.Send(LogType.Information, "", String.Format("{0} is enabled", key));
                     result.Add(type);
                     result.Add(type);
-                    bChanged = true;
-                }
+                else
+                    Logger.Send(LogType.Information, "", string.Format("Entity [{0}] is disabled", key));
             }
             }
+            else
+            {
+                //Logger.Send(LogType.Information, "", String.Format("{0} does not exist - enabling", key));
 
 
-            if (bChanged)
-                new LocalConfiguration<ModuleConfiguration>(Path.GetDirectoryName(path) ?? "", Path.GetFileName(path)).Save(config);
-            return result.ToArray();
+                config[key] = true;
+                result.Add(type);
+                bChanged = true;
+            }
         }
         }
 
 
-        public static bool IsSupported<T>() where T : Entity
-        {
-            _dbtypes ??= LoadSupportedTypes();
-            return _dbtypes.Contains(typeof(T));
-        }
+        if (bChanged)
+            new LocalConfiguration<ModuleConfiguration>(Path.GetDirectoryName(path) ?? "", Path.GetFileName(path)).Save(config);
+        return result.ToArray();
+    }
 
 
-        #endregion
+    public static bool IsSupported<T>() where T : Entity
+    {
+        _dbtypes ??= LoadSupportedTypes();
+        return _dbtypes.Contains(typeof(T));
+    }
 
 
-        //public static void OpenSession(bool write)
-        //{
-        //	Provider.OpenSession(write);
-        //}
+    #endregion
 
 
-        //public static void CloseSession()
-        //{
-        //	Provider.CloseSession();
-        //}
+    //public static void OpenSession(bool write)
+    //{
+    //	Provider.OpenSession(write);
+    //}
 
 
-        #region Private Methods
+    //public static void CloseSession()
+    //{
+    //	Provider.CloseSession();
+    //}
 
 
-        public static void LoadScripts()
-        {
-            Logger.Send(LogType.Information, "", "Loading Script Cache...");
-            LoadedScripts.Clear();
-            var scripts = Provider.Load(
-                new Filter<Script>
-                        (x => x.ScriptType).IsEqualTo(ScriptType.BeforeQuery)
-                    .Or(x => x.ScriptType).IsEqualTo(ScriptType.AfterQuery)
-                    .Or(x => x.ScriptType).IsEqualTo(ScriptType.BeforeSave)
-                    .Or(x => x.ScriptType).IsEqualTo(ScriptType.AfterSave)
-                    .Or(x => x.ScriptType).IsEqualTo(ScriptType.BeforeDelete)
-                    .Or(x => x.ScriptType).IsEqualTo(ScriptType.AfterDelete)
-                    .Or(x => x.ScriptType).IsEqualTo(ScriptType.AfterLoad)
-            );
-            foreach (var script in scripts)
+    #region Private Methods
+
+    public static void LoadScripts()
+    {
+        Logger.Send(LogType.Information, "", "Loading Script Cache...");
+        LoadedScripts.Clear();
+        var scripts = Provider.Load(
+            new Filter<Script>
+                    (x => x.ScriptType).IsEqualTo(ScriptType.BeforeQuery)
+                .Or(x => x.ScriptType).IsEqualTo(ScriptType.AfterQuery)
+                .Or(x => x.ScriptType).IsEqualTo(ScriptType.BeforeSave)
+                .Or(x => x.ScriptType).IsEqualTo(ScriptType.AfterSave)
+                .Or(x => x.ScriptType).IsEqualTo(ScriptType.BeforeDelete)
+                .Or(x => x.ScriptType).IsEqualTo(ScriptType.AfterDelete)
+                .Or(x => x.ScriptType).IsEqualTo(ScriptType.AfterLoad)
+        );
+        foreach (var script in scripts)
+        {
+            var key = string.Format("{0} {1}", script.Section, script.ScriptType.ToString());
+            var doc = new ScriptDocument(script.Code);
+            if (doc.Compile())
             {
             {
-                var key = string.Format("{0} {1}", script.Section, script.ScriptType.ToString());
-                var doc = new ScriptDocument(script.Code);
-                if (doc.Compile())
-                {
-                    Logger.Send(LogType.Information, "",
-                        string.Format("- {0}.{1} Compiled Successfully", script.Section, script.ScriptType.ToString()));
-                    LoadedScripts[key] = doc;
-                }
-                else
-                {
-                    Logger.Send(LogType.Error, "",
-                        string.Format("- {0}.{1} Compile Exception:\n{2}", script.Section, script.ScriptType.ToString(), doc.Result));
-                }
+                Logger.Send(LogType.Information, "",
+                    string.Format("- {0}.{1} Compiled Successfully", script.Section, script.ScriptType.ToString()));
+                LoadedScripts[key] = doc;
+            }
+            else
+            {
+                Logger.Send(LogType.Error, "",
+                    string.Format("- {0}.{1} Compile Exception:\n{2}", script.Section, script.ScriptType.ToString(), doc.Result));
             }
             }
-
-            Logger.Send(LogType.Information, "", "Loading Script Cache Complete");
         }
         }
 
 
-        //private static Type[] entities = null;
-        //private static void SetEntityTypes(Type[] types)
-        //{
-        //	foreach (Type type in types)
-        //	{
-        //		if (!type.IsSubclassOf(typeof(Entity)))
-        //			throw new Exception(String.Format("{0} is not a valid entity", type.Name));
-        //	}
-        //	entities = types;
-        //}
+        Logger.Send(LogType.Information, "", "Loading Script Cache Complete");
+    }
 
 
-        private static Type[] stores = { };
+    //private static Type[] entities = null;
+    //private static void SetEntityTypes(Type[] types)
+    //{
+    //	foreach (Type type in types)
+    //	{
+    //		if (!type.IsSubclassOf(typeof(Entity)))
+    //			throw new Exception(String.Format("{0} is not a valid entity", type.Name));
+    //	}
+    //	entities = types;
+    //}
 
 
-        private static void SetStoreTypes(Type[] types)
-        {
-            types = types.Where(
-                myType => myType.IsClass
-                    && !myType.IsAbstract
-                    && !myType.IsGenericType).ToArray();
-            foreach (var type in types)
-                if (!type.GetInterfaces().Contains(typeof(IStore)))
-                    throw new Exception(string.Format("{0} is not a valid store", type.Name));
-            stores = types;
-        }
+    private static Type[] stores = { };
 
 
-        private static Type[] ConsolidatedObjectModel()
-        {
-            // Add the core types from InABox.Core
-            var types = new List<Type>();
-            //var coreTypes = CoreUtils.TypeList(
-            //	new Assembly[] { typeof(Entity).Assembly },
-            //	myType =>
-            //	myType.IsClass
-            //	&& !myType.IsAbstract
-            //	&& !myType.IsGenericType
-            //	&& myType.IsSubclassOf(typeof(Entity))
-            //	&& myType.GetInterfaces().Contains(typeof(IRemotable))
-            //);
-            //types.AddRange(coreTypes);
-
-            // Now add the end-user object model
-            types.AddRange(Entities.Where(x =>
-                x.GetTypeInfo().IsClass
-                && !x.GetTypeInfo().IsGenericType
-                && x.GetTypeInfo().IsSubclassOf(typeof(Entity))
-            ));
-
-            return types.ToArray();
-        }
+    private static void SetStoreTypes(Type[] types)
+    {
+        types = types.Where(
+            myType => myType.IsClass
+                && !myType.IsAbstract
+                && !myType.IsGenericType).ToArray();
+        foreach (var type in types)
+            if (!type.GetInterfaces().Contains(typeof(IStore)))
+                throw new Exception(string.Format("{0} is not a valid store", type.Name));
+        stores = types;
+    }
 
 
-        private enum SchemaStatus
-        {
-            New,
-            Changed,
-            Validated
-        }
+    private static Type[] ConsolidatedObjectModel()
+    {
+        // Add the core types from InABox.Core
+        var types = new List<Type>();
+        //var coreTypes = CoreUtils.TypeList(
+        //	new Assembly[] { typeof(Entity).Assembly },
+        //	myType =>
+        //	myType.IsClass
+        //	&& !myType.IsAbstract
+        //	&& !myType.IsGenericType
+        //	&& myType.IsSubclassOf(typeof(Entity))
+        //	&& myType.GetInterfaces().Contains(typeof(IRemotable))
+        //);
+        //types.AddRange(coreTypes);
+
+        // Now add the end-user object model
+        types.AddRange(Entities.Where(x =>
+            x.GetTypeInfo().IsClass
+            && !x.GetTypeInfo().IsGenericType
+            && x.GetTypeInfo().IsSubclassOf(typeof(Entity))
+        ));
+
+        return types.ToArray();
+    }
 
 
-        private static Dictionary<string, Type> GetSchema()
-        {
-            var model = new Dictionary<string, Type>();
-            var objectmodel = ConsolidatedObjectModel();
-            foreach (var type in objectmodel)
-            {
-                Dictionary<string, Type> thismodel = CoreUtils.PropertyList(type, x => true, true);
-                foreach (var key in thismodel.Keys)
-                    model[type.Name + "." + key] = thismodel[key];
-            }
+    private enum SchemaStatus
+    {
+        New,
+        Changed,
+        Validated
+    }
 
 
-            return model;
-            //return Serialization.Serialize(model, Formatting.Indented);
+    private static Dictionary<string, Type> GetSchema()
+    {
+        var model = new Dictionary<string, Type>();
+        var objectmodel = ConsolidatedObjectModel();
+        foreach (var type in objectmodel)
+        {
+            Dictionary<string, Type> thismodel = CoreUtils.PropertyList(type, x => true, true);
+            foreach (var key in thismodel.Keys)
+                model[type.Name + "." + key] = thismodel[key];
         }
         }
 
 
-        private static SchemaStatus ValidateSchema()
-        {
-            var db_schema = Provider.GetSchema();
-            if (db_schema.Count() == 0)
-                return SchemaStatus.New;
+        return model;
+        //return Serialization.Serialize(model, Formatting.Indented);
+    }
 
 
-            var mdl_json = Serialization.Serialize(GetSchema());
-            var db_json = Serialization.Serialize(db_schema);
-            return mdl_json.Equals(db_json) ? SchemaStatus.Validated : SchemaStatus.Changed;
-        }
+    private static SchemaStatus ValidateSchema()
+    {
+        var db_schema = Provider.GetSchema();
+        if (db_schema.Count() == 0)
+            return SchemaStatus.New;
 
 
-        private static void SaveSchema()
-        {
-            Provider.SaveSchema(GetSchema());
-        }
+        var mdl_json = Serialization.Serialize(GetSchema());
+        var db_json = Serialization.Serialize(db_schema);
+        return mdl_json.Equals(db_json) ? SchemaStatus.Validated : SchemaStatus.Changed;
+    }
 
 
-        #endregion
+    private static void SaveSchema()
+    {
+        Provider.SaveSchema(GetSchema());
     }
     }
+
+    #endregion
 }
 }

+ 1 - 1
InABox.Server/RPC/Handlers/Info.cs

@@ -12,7 +12,7 @@ namespace InABox.Rpc
         {
         {
             var response = new RpcInfoResult()
             var response = new RpcInfoResult()
             {
             {
-                Info = new DatabaseInfo(DbFactory.ColorScheme, DbFactory.Logo, CoreUtils.GetVersion(), Sender.IsSecure(), DbFactory.RestPort, DbFactory.RPCPort)
+                Info = new DatabaseInfo(DbFactory.ColorScheme, DbFactory.Logo, CoreUtils.GetVersion(), Sender.IsSecure(), DbFactory.RestPort, DbFactory.RPCPort, DbFactory.ID)
             };
             };
             
             
             return response;
             return response;

+ 1 - 1
InABox.Server/RestService.cs

@@ -208,7 +208,7 @@ namespace InABox.API
 
 
         public static InfoResponse Info(InfoRequest request)
         public static InfoResponse Info(InfoRequest request)
         {
         {
-            var response = new InfoResponse(new DatabaseInfo(DbFactory.ColorScheme, DbFactory.Logo, CoreUtils.GetVersion(), IsHTTPS, DbFactory.RestPort, DbFactory.RPCPort));
+            var response = new InfoResponse(new DatabaseInfo(DbFactory.ColorScheme, DbFactory.Logo, CoreUtils.GetVersion(), IsHTTPS, DbFactory.RestPort, DbFactory.RPCPort, DbFactory.ID));
             response.Status = StatusCode.OK;
             response.Status = StatusCode.OK;
             return response;
             return response;
         }
         }