Ver Fonte

Fixing problems with the database columns being wrong after my new changes

Kenric Nugteren há 7 meses atrás
pai
commit
cc35fb9349

+ 4 - 14
InABox.Core/BaseObject.cs

@@ -586,14 +586,9 @@ namespace InABox.Core
 
             foreach(var property in DatabaseSchema.Properties(sender.GetType()))
             {
-                if (property.IsDBColumn)
+                if (property.IsDBColumn && (all || sender.HasOriginalValue(property.Name)))
                 {
-                    var isLocal = !property.HasParentEntityLink()
-                        || (property.Parent?.HasParentEntityLink() != true && property.Name.EndsWith(".ID"));
-                    if (isLocal && (all || sender.HasOriginalValue(property.Name)))
-                    {
-                        result[property.Name] = property.Getter()(sender);
-                    }
+                    result[property.Name] = property.Getter()(sender);
                 }
             }
 
@@ -606,14 +601,9 @@ namespace InABox.Core
 
             foreach(var property in DatabaseSchema.Properties(sender.GetType()))
             {
-                if (property.IsDBColumn)
+                if (property.IsDBColumn && sender.OriginalValueList.TryGetValue(property.Name, out var value))
                 {
-                    var isLocal = !property.HasParentEntityLink()
-                        || (property.Parent?.HasParentEntityLink() != true && property.Name.EndsWith(".ID"));
-                    if (isLocal && sender.OriginalValueList.TryGetValue(property.Name, out var value))
-                    {
-                        result[property.Name] = value;
-                    }
+                    result[property.Name] = value;
                 }
             }
 

+ 26 - 107
InABox.Core/CoreUtils.cs

@@ -29,47 +29,6 @@ namespace InABox.Core
         SQLite
     }
 
-    //public class DateTimeJsonConverter : JsonConverter<DateTime>
-    //{
-
-    //    public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)
-    //    {
-    //        String result = String.Format("{0:o}", value); 
-    //        writer.WriteValue(result);
-    //    }
-
-    //    public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
-    //    {
-    //        if (reader.Value is DateTime)
-    //            return (DateTime)reader.Value;
-    //        else
-    //        {
-    //            String value = reader.Value.ToString().Replace("\"","");
-    //            DateTime.TryParse(value, out DateTime result);
-    //            return result;
-    //        }
-    //    }
-
-    //    public override bool CanRead => true;
-    //    public override bool CanWrite => true;
-    //}
-
-    //public class TimeSpanJsonConverter : JsonConverter<TimeSpan>
-    //{
-
-    //    public override void WriteJson(JsonWriter writer, TimeSpan value, JsonSerializer serializer)
-    //    {
-    //        TimeSpan timespan = (TimeSpan)value;
-    //        writer.WriteValue(timespan.Ticks.ToString());
-    //    }
-
-    //    public override TimeSpan ReadJson(JsonReader reader, Type objectType, TimeSpan existingValue, bool hasExistingValue, JsonSerializer serializer)
-    //    {
-    //        Int64 ticks = Int64.Parse(reader.Value.ToString().Replace("\"", ""));
-    //        return new TimeSpan(ticks);
-    //    }
-    //}
-
     public enum SyncfusionVersion
     {
         v16_3,
@@ -1422,24 +1381,25 @@ namespace InABox.Core
         public static Columns<T> GetColumns<T>(Columns<T>? columns)
             => (GetColumns(typeof(T), columns) as Columns<T>)!;
         
+        /// <summary>
+        /// Ensures that <paramref name="columns"/> is not <see langword="null"/>, by setting it to <b>all</b> columns that are
+        /// writable, serializable and persistable if it is.<br/>
+        /// If <paramref name="columns"/> is not <see langword="null"/>, it is returned unchanged.
+        /// </summary>
         public static IColumns GetColumns(Type T, IColumns? columns)
         {
-            //var cols = columns;
             if (columns == null || columns.Count == 0)
             {
                 if (!_columnscache.TryGetValue(T, out var result))
                 {
                     result = Columns.None(T);
-                    var props = PropertyList(T,
-                        x =>
-                            x.GetCustomAttribute<DoNotSerialize>() == null &&
-                            x.GetCustomAttribute<DoNotPersist>() == null &&
-                            x.CanWrite && x.PropertyType != typeof(UserProperties), true);
-                    foreach (var prop in props)
-                        result.Add(prop.Key);
-                    var custom = DatabaseSchema.Properties(T).Where(x => x is CustomProperty);
-                    foreach (var prop in custom)
-                        result.Add(prop.Name);
+                    foreach(var prop in DatabaseSchema.Properties(T))
+                    {
+                        if (prop is StandardProperty stdProp
+                            && (!stdProp.IsPersistable || !stdProp.IsSerializable || !stdProp.Property.CanWrite))
+                            continue;
+                        result.Add(prop);
+                    }
                     _columnscache[T] = result;
                 }
 
@@ -1447,67 +1407,26 @@ namespace InABox.Core
             }
 
             return columns;
-            //{
-            //    foreach (var col in cols.Items)
-            //    {
-            //        try
-            //        {
-            //            result.Add(col.Property);
-
-            //            //PropertyInfo prop = null;
-            //            //IProperty iprop = null;
-            //            //if (col.Property != null)
-            //            //{
-            //            //    try
-            //            //    {
-            //            //        iprop = DatabaseSchema.Property(typeof(T), col.Property);
-            //            //    }
-            //            //    catch (Exception e2)
-            //            //    {
-            //            //    }
-            //            //    if ((iprop != null) && (iprop is CustomProperty))
-            //            //        result.Add(col.Property);
-            //            //    else
-            //            //    {
-            //            //        prop = CoreUtils.GetProperty(typeof(T), col.Property);
-            //            //        if ((prop.GetCustomAttribute<DoNotSerialize>() == null) && (prop.GetCustomAttribute<DoNotPersist>() == null) && prop.CanWrite)
-            //            //            result.Add(col.Property);
-            //            //    }
-            //            //}
-            //            //else
-            //            //{
-            //            //    String property = CoreUtils.GetFullPropertyName((MemberExpression)col.Expression, ".");
-            //            //    prop = CoreUtils.GetProperty(typeof(T), property);
-            //            //    if ((prop.GetCustomAttribute<DoNotSerialize>() == null) && (prop.GetCustomAttribute<DoNotPersist>() == null) && prop.CanWrite)
-            //            //        result.Add(property);
-            //            //}
-
-
-            //        }
-            //        catch (Exception e)
-            //        {
-
-            //        }
-            //    }
-            //}
-            //return result;
         }
 
-        public static List<string> GetColumnNames(Type T, Func<PropertyInfo, bool> Predicate)
+        /// <summary>
+        /// Return a list of the names of the columns of <paramref name="T"/> which are serializable, persistable and writable (i.e., a
+        /// <see langword="null"/> set of columns), <i>and</i> that match the <paramref name="Predicate"/>.
+        /// </summary>
+        /// <param name="T"></param>
+        /// <param name="Predicate"></param>
+        /// <returns></returns>
+        public static List<string> GetColumnNames(Type T, Func<IProperty, bool> predicate)
         {
-            //var cols = columns;
             var result = new List<string>();
 
-            var props = PropertyList(T,
-                x =>
-                    x.GetCustomAttribute<DoNotSerialize>() == null &&
-                    x.GetCustomAttribute<DoNotPersist>() == null &&
-                    x.CanWrite && x.PropertyType != typeof(UserProperties) && Predicate(x), true);
-            foreach (var prop in props)
-                result.Add(prop.Key);
-            var custom = DatabaseSchema.Properties(T).Where(x => x is CustomProperty);
-            foreach (var prop in custom)
+            foreach(var prop in DatabaseSchema.Properties(T))
+            {
+                if ((prop is StandardProperty stdProp && (!stdProp.IsPersistable || !stdProp.IsSerializable || !stdProp.Property.CanWrite))
+                    || !predicate(prop))
+                    continue;
                 result.Add(prop.Name);
+            }
 
             return result;
         }

+ 2 - 3
InABox.Core/DataModel/AutoDataModel.cs

@@ -76,8 +76,7 @@ namespace InABox.Core
                     var target = CoreUtils.GetPropertyExpression(map, prop.Name + ".ID", typeof(object));
 
                     var cols = Core.Columns.None(map);
-                    var columnList = CoreUtils.GetColumnNames(map, 
-                        x => !x.DeclaringType.IsAssignableFrom(map) || x.Name.Split('.', StringSplitOptions.None).First() != prop.Name);
+                    var columnList = CoreUtils.GetColumnNames(map, x => x.GetOuterParent(x => x.IsEntityLink)?.Name != prop.Name);
                     foreach (var col in columnList) cols.Add(col);
                     cols.Add(prop.Name + ".ID");
 
@@ -128,7 +127,7 @@ namespace InABox.Core
                     var lookupAlias = headName + "_" + TableName(map);
 
                     var cols = Core.Columns.None(map);
-                    var columnList = CoreUtils.GetColumnNames(map, x => !x.DeclaringType.IsAssignableFrom(map) || x.Name != prop.Name);
+                    var columnList = CoreUtils.GetColumnNames(map, x => x.GetOuterParent(x => x.IsEntityLink)?.Name != prop.Name);
                     foreach (var col in columnList) cols.Add(col);
                     cols.Add(prop.Name + ".ID");
 

+ 10 - 0
InABox.Core/DatabaseSchema/CustomProperty.cs

@@ -244,6 +244,16 @@ namespace InABox.Core
         [DoNotSerialize]
         public bool IsCalculated => false;
 
+        [NullEditor]
+        [DoNotPersist]
+        [DoNotSerialize]
+        public bool IsPersistable => true;
+
+        [NullEditor]
+        [DoNotPersist]
+        [DoNotSerialize]
+        public bool IsSerializable => true;
+
         [NullEditor]
         [DoNotPersist]
         [DoNotSerialize]

+ 11 - 0
InABox.Core/DatabaseSchema/IProperty.cs

@@ -34,6 +34,17 @@ namespace InABox.Core
 
         bool IsCalculated { get; }
 
+        bool IsPersistable { get; }
+
+        bool IsSerializable { get; }
+
+        /// <summary>
+        /// Returns <see langword="true"/> if this is a column to be stored in a database table.
+        /// </summary>
+        /// <remarks>
+        /// This is equivalent to the property being a <see cref="CustomProperty"/> or a <see cref="StandardProperty"/> which
+        /// is non-calculated, persistable, writeable and is a <i>local property</i>.
+        /// </remarks>
         bool IsDBColumn { get; }
 
         /// <summary>

+ 17 - 4
InABox.Core/DatabaseSchema/StandardProperty.cs

@@ -150,14 +150,26 @@ namespace InABox.Core
             }
         }
 
-        private bool IsPersistable(StandardProperty? prop)
+        private bool IsPropertyPersistable(StandardProperty? prop)
         {
             if (prop == null)
                 return true;
             if (prop.Property.GetCustomAttribute<DoNotPersist>() != null)
                 return false;
-            return IsPersistable(prop.Parent as StandardProperty);
+            return IsPropertyPersistable(prop.Parent as StandardProperty);
         }
+        private bool IsPropertySerializable(StandardProperty? prop)
+        {
+            if (prop == null)
+                return true;
+            if (prop.Property.GetCustomAttribute<DoNotSerialize>() != null)
+                return false;
+            return IsPropertySerializable(prop.Parent as StandardProperty);
+        }
+
+        public bool IsPersistable => IsPropertyPersistable(this);
+
+        public bool IsSerializable => IsPropertySerializable(this);
         
         private bool? _dbColumn;
         public bool IsDBColumn
@@ -167,8 +179,9 @@ namespace InABox.Core
                 if (!_dbColumn.HasValue)
                 {
                     _dbColumn = !IsCalculated
-                        && IsPersistable(this)
-                        && Property.CanWrite;
+                        && IsPersistable
+                        && Property.CanWrite
+                        && (!this.HasParentEntityLink() || (Parent?.HasParentEntityLink() != true && Property.Name == "ID"));
                 }
                 return _dbColumn.Value;
             }

+ 2 - 14
InABox.Core/Deletion.cs

@@ -30,24 +30,12 @@ namespace InABox.Core
 
         public Dictionary<string, CoreTable> Cascades { get; set; } = new Dictionary<string, CoreTable>();
 
-        private static bool IsDeletionColumn(IProperty property)
-        {
-            if (property.IsCalculated) return false;
-            if (property is StandardProperty standardProperty && standardProperty.Property.GetCustomAttribute<DoNotPersist>() != null)
-                return false;
-            if (property.Parent is null) return true;
-            if (property.Parent.IsEntityLink && !property.Name.EndsWith(".ID")) return false;
-            if (property.Parent.HasParentEntityLink()) return false;
-            return true;
-        }
-
         public static IColumns DeletionColumns(Type T)
         {
             var columns = Columns.None(T);
-            foreach(var property in DatabaseSchema.Properties(T))
+            foreach(var property in DatabaseSchema.Properties(T).Where(x => x.IsDBColumn))
             {
-                if (IsDeletionColumn(property))
-                    columns.Add(property);
+                columns.Add(property);
             }
             return columns;
         }

+ 1 - 10
InABox.Core/Serialization.cs

@@ -648,18 +648,9 @@ namespace InABox.Core
             return (result != null ? (T)result : default)!;
         }
 
-        private static bool IsSerializable(Type type,  StandardProperty? prop)
-        {
-            if (prop == null)
-                return true;
-            if (prop.Property.GetCustomAttribute<DoNotSerialize>() != null)
-                return false;
-            return IsSerializable(type, prop.Parent as StandardProperty);
-        }
-        
         public static IEnumerable<IProperty> SerializableProperties(Type type) =>
             DatabaseSchema.Properties(type)
-                .Where(x => !(x is StandardProperty st) || IsSerializable(type,st));
+                .Where(x => !(x is StandardProperty st) || st.IsSerializable);
 
         private static void WriteOriginalValues<TObject>(this CoreBinaryWriter writer, TObject obj)
             where TObject : BaseObject

+ 0 - 9
InABox.Database/DbFactory.cs

@@ -96,15 +96,6 @@ public static class DbFactory
             throw new Exception("Database migration failed. Aborting startup");
         }
 
-        //DataUpdater.DoSpecificMigration(new VersionNumber(8, 23), new VersionNumber(8, 24));
-
-        //Load up your custom properties here!
-        // Can't use clients (b/c we're 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 = ProviderFactory.NewProvider(Logger.Main).Query<CustomProperty>().ToArray<CustomProperty>();
-        DatabaseSchema.Load(props);
-
         AssertLicense();
         BeginLicenseCheckTimer();
 

+ 12 - 49
inabox.database.sqlite/SQLiteProvider.cs

@@ -336,6 +336,7 @@ public class SQLiteProviderFactory : IProviderFactory
         }
 
         var customproperties = MainProvider.Load<CustomProperty>(); // new Filter<CustomProperty>(x => x.Class).IsEqualTo(type.EntityName()))
+        DatabaseSchema.Load(customproperties);
 
         metadata = LoadMetaData();
 
@@ -649,69 +650,31 @@ public class SQLiteProviderFactory : IProviderFactory
         Type definition = view?.Generator != null
             ? view.Generator.Definition
             : type;
-        
-        var properties = CoreUtils.GetInheritedProperties(definition).Where(x =>
-            x.GetCustomAttribute<DoNotPersist>() == null
-            // We think this is wrong; but who knows? && x.GetCustomAttribute<DoNotSerialize>() == null
-            && x.GetCustomAttributes().FirstOrDefault(a => a.GetType().Equals(typeof(AggregateAttribute))) == null
-            && x.GetCustomAttributes().FirstOrDefault(a => a.GetType().Equals(typeof(FormulaAttribute))) == null
-            && x.GetCustomAttributes().FirstOrDefault(a => a.GetType().Equals(typeof(ConditionAttribute))) == null
-            && x.GetCustomAttributes().FirstOrDefault(a => a.GetType().Equals(typeof(ComplexFormulaAttribute))) == null
-            && x.GetCustomAttributes().FirstOrDefault(a => a.GetType().Equals(typeof(ChildEntityAttribute))) == null
-            && x.CanWrite
-            && x.PropertyType != typeof(UserProperties)
-        );
-
-        foreach (var property in properties)
-            if (property.PropertyType.GetInterfaces().Contains(typeof(IEnclosedEntity)))
-            {
-                LoadFields(property.PropertyType, fields, prefixes.Concat(new[] { property }).ToList(), customproperties);
-            }
-            else if (property.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)))
-            {
-                var subprop = property.PropertyType.GetProperty("ID")!; // Not-null because IEntityLink has ID
-                var subname = ColumnName(prefixes.Concat(new[] { property, subprop }).ToArray());
-                var subtype = ColumnType(subprop.PropertyType);
-                fields[subname] = subtype;
-            }
-            else
-            {
-                var colname = ColumnName(prefixes.Concat(new[] { property }).ToArray());
-                var coltype = ColumnType(property.PropertyType);
-                fields[colname] = string.Format("{0}{1}", coltype, colname.Equals("ID") ? " PRIMARY KEY" : "");
-            }
 
-        // Now add in the Custom Properties (if any exist)
-        //CustomProperty[] customprops = Load<CustomProperty>(new Filter<CustomProperty>(x=>x.Class).IsEqualTo(type.EntityName()));
-        foreach (var prop in customproperties.Where(x => x.Class.Equals(type.EntityName())))
-            fields[prop.Name] = ColumnType(prop.PropertyType);
+        foreach(var property in DatabaseSchema.Properties(definition).Where(x => x.IsDBColumn))
+        {
+            fields[property.Name] =
+                ColumnType(property.PropertyType)
+                + (property.Name.Equals("ID") ? " PRIMARY KEY" : "");
+        }
     }
 
     private List<string> LoadIndexes(Type type)
     {
         var result = new List<string>();
 
-        var properties = type.GetProperties().Where(x =>
-            x.GetCustomAttribute<DoNotSerialize>() == null && x.GetCustomAttribute<DoNotPersist>() == null &&
-            x.GetCustomAttributes().FirstOrDefault(a => a.GetType().IsSubclassOf(typeof(AggregateAttribute))) == null
-            && x.GetCustomAttributes().FirstOrDefault(a => a.GetType().Equals(typeof(ChildEntityAttribute))) == null
-            && x.CanWrite);
-        foreach (var property in properties)
-            if (property.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)))
+        foreach (var property in DatabaseSchema.Properties(type).Where(x => x.IsDBColumn))
+            if (property.Parent?.IsEntityLink == true && property.Name.EndsWith(".ID"))
             {
-                var subprop = property.PropertyType.GetProperty("ID")!; // Not-null because IEntityLink has ID
-                var subname = ColumnName(property, subprop);
                 var tablename = type.EntityName().Split('.').Last();
-                result.Add(string.Format("CREATE INDEX idx{0}{1} ON {0} ([{2}])", tablename, subname.Replace(".", ""), subname));
+                result.Add(string.Format("CREATE INDEX idx{0}{1} ON {0} ([{2}])", tablename, property.Name.Replace(".", ""), property.Name));
             }
             else
             {
-                var index = property.GetCustomAttributes<SecondaryIndexAttribute>().FirstOrDefault();
-                if (index != null)
+                if (property.HasAttribute<SecondaryIndexAttribute>())
                 {
-                    var colname = ColumnName(property);
                     var tablename = type.EntityName().Split('.').Last();
-                    result.Add(string.Format("CREATE INDEX idx{0}{1} ON {0} ([{2}])", tablename, colname.Replace(".", ""), colname));
+                    result.Add(string.Format("CREATE INDEX idx{0}{1} ON {0} ([{2}])", tablename, property.Name.Replace(".", ""), property.Name));
                 }
             }
 

+ 1 - 1
inabox.wpf/DynamicGrid/Editors/FilterEditor/Nodes/PropertyNode.cs

@@ -23,7 +23,7 @@ public class PropertyNode<T> : ComboBox
         MinWidth = 100;
 
         var properties = DatabaseSchema.Properties(typeof(T))
-            .Where(x => !x.HasAttribute<DoNotSerialize>())
+            .Where(x => x.IsSerializable)
             .Select(x => x.Name)
             .ToList();
         properties.Sort();