Bläddra i källkod

Added cascading for DoPropertyChanged and OriginalValues and LoadedColumns

Kenric Nugteren 8 månader sedan
förälder
incheckning
1729a994dd

+ 106 - 24
InABox.Core/BaseObject.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -26,6 +27,83 @@ namespace InABox.Core
         public void SetObserving(bool active);
     }
 
+    public interface IOriginalValues : IEnumerable<KeyValuePair<string, object?>>
+    {
+        public object? this[string key] { get; set; }
+
+        public bool ContainsKey(string key);
+
+        public void Clear();
+
+        public bool TryGetValue(string key, out object? value);
+
+        public bool TryAdd(string key, object? value);
+
+        public void Remove(string key);
+
+        public object? GetValueOrDefault(string key)
+        {
+            if(TryGetValue(key, out object? value)) return value;
+            return null;
+        }
+    }
+    public class OriginalValues : IOriginalValues
+    {
+        private readonly ConcurrentDictionary<string, object?> Dictionary = new ConcurrentDictionary<string, object?>();
+
+        public object? this[string key] { get => Dictionary[key]; set => Dictionary[key] = value; }
+
+        public void Clear()
+        {
+            Dictionary.Clear();
+        }
+
+        public bool ContainsKey(string key) => Dictionary.ContainsKey(key);
+
+        public IEnumerator<KeyValuePair<string, object?>> GetEnumerator()
+        {
+            return Dictionary.GetEnumerator();
+        }
+
+        public bool TryGetValue(string key, out object? value)
+        {
+            return Dictionary.TryGetValue(key, out value);
+        }
+
+        public bool TryAdd(string key, object? value)
+        {
+            return Dictionary.TryAdd(key, value);
+        }
+
+        public void Remove(string key)
+        {
+            (Dictionary as IDictionary<string, object?>).Remove(key);
+        }
+
+        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+    }
+
+    public interface ILoadedColumns
+    {
+        public bool Add(string key);
+
+        public bool Contains(string key);
+    }
+    public class LoadedColumns : ILoadedColumns
+    {
+        private HashSet<string> Columns = new HashSet<string>();
+
+        public bool Add(string key)
+        {
+            return Columns.Add(key);
+        }
+
+        public bool Contains(string key)
+        {
+            return Columns.Contains(key);
+        }
+    }
+
     /// <summary>
     ///     Observable object with INotifyPropertyChanged implemented
     /// </summary>
@@ -57,9 +135,7 @@ namespace InABox.Core
 
         protected virtual void Init()
         {
-            CheckOriginalValues();
-
-            LoadedColumns = new HashSet<string>();
+            LoadedColumns = CreateLoadedColumns();
 
             UserProperties = new UserProperties();
             //UserProperties.ParentType = this.GetType();
@@ -85,7 +161,7 @@ namespace InABox.Core
 
         public static bool GlobalObserving = true;
 
-        private bool _observing = true;
+        private bool _observing = false;
 
         public bool IsObserving()
         {
@@ -111,11 +187,19 @@ namespace InABox.Core
         private bool bChanged;
 
         [DoNotPersist]
-        public ConcurrentDictionary<string, object?> OriginalValues { get; set; }
+        private IOriginalValues _originalValues;
+        public IOriginalValues OriginalValues
+        {
+            get
+            {
+                _originalValues ??= CreateOriginalValues();
+                return _originalValues;
+            }
+        }
 
         [DoNotPersist]
         [DoNotSerialize]
-        public HashSet<string> LoadedColumns { get; set; }
+        public ILoadedColumns LoadedColumns { get; set; }
 
         protected virtual void SetChanged(string name, object? before, object? after)
         {
@@ -140,11 +224,15 @@ namespace InABox.Core
             }
         }
 
+        // This function is *only* meant to be called by EnclosedEntity and EntityLink
+        internal void CascadePropertyChanged(string name, object? before, object? after)
+        {
+            SetChanged(name, before, after);
+        }
 
         private bool QueryChanged()
         {
-            CheckOriginalValues();
-            if (OriginalValues.Count > 0)
+            if (OriginalValues.Any())
                 return true;
 
             foreach (var oo in DatabaseSchema.GetSubObjects(this))
@@ -173,21 +261,18 @@ namespace InABox.Core
             if (!BaseObjectExtensions.HasChanged(before, after))
                 return;
 
-            CheckOriginalValues();
             if (!OriginalValues.ContainsKey(name))
                 OriginalValues[name] = before;
             SetChanged(name, before, after);
         }
 
-        private void CheckOriginalValues()
+        protected virtual IOriginalValues CreateOriginalValues()
         {
-            if (OriginalValues == null)
-            {
-                var bObserving = _observing;
-                _observing = false;
-                OriginalValues = new ConcurrentDictionary<string, object?>();
-                _observing = bObserving;
-            }
+            return new OriginalValues();
+        }
+        protected virtual ILoadedColumns CreateLoadedColumns()
+        {
+            return new LoadedColumns();
         }
 
         public bool IsChanged()
@@ -201,13 +286,12 @@ namespace InABox.Core
             var bObs = IsObserving();
             SetObserving(false);
 
-            CheckOriginalValues();
-            foreach (var key in OriginalValues.Keys.ToArray())
+            foreach (var (key, value) in OriginalValues)
             {
                 try
                 {
                     var prop = key.Contains(".") 
-                        ? CoreUtils.GetProperty(GetType(),key) 
+                        ? CoreUtils.GetProperty(GetType(), key) 
                         : GetType().GetRuntimeProperty(key);
                     if(prop != null)
                     {
@@ -222,7 +306,7 @@ namespace InABox.Core
                     }
                     else if (UserProperties.ContainsKey(key))
                     {
-                        UserProperties[key] = OriginalValues[key];
+                        UserProperties[key] = value;
                     }
                     else
                     {
@@ -251,7 +335,6 @@ namespace InABox.Core
         {
             bApplyingChanges = true;
 
-            CheckOriginalValues();
             OriginalValues.Clear();
 
             bChanged = false;
@@ -268,8 +351,7 @@ namespace InABox.Core
             var type = GetType();
             try
             {
-                CheckOriginalValues();
-                foreach (var key in OriginalValues.Keys)
+                foreach (var (key, _) in OriginalValues)
                     try
                     {
                         if (UserProperties.ContainsKey(key))

+ 0 - 2
InABox.Core/CoreUtils.cs

@@ -875,8 +875,6 @@ namespace InABox.Core
                 //if (method == null)
                 var oldval = propertyToSet.GetValue(o);
                 propertyToSet.SetValue(o, convertedvalue, null);
-                if (o is BaseObject)
-                    ((BaseObject)o).OnPropertyChanged(propertyToSet.Name, oldval, convertedvalue);
 
                 //else
                 //    method.Invoke(o, new Object[] { convertedvalue });

+ 14 - 5
InABox.Core/EnclosedEntity.cs

@@ -25,18 +25,27 @@ namespace InABox.Core
             _linkedPath = path;
         }
 
+        protected override IOriginalValues CreateOriginalValues()
+        {
+            return new SubObjectOriginalValues(this);
+        }
+
+        protected override ILoadedColumns CreateLoadedColumns()
+        {
+            return new SubObjectLoadedColumns(this);
+        }
+
         public BaseObject GetLinkedParent() => _linkedParent;
 
         public string GetLinkedPath() => _linkedPath;
         
         protected override void DoPropertyChanged(string name, object? before, object? after)
         {
-            if (IsObserving())
+            LinkedProperties.GetParent(this).CascadePropertyChanged(LinkedProperties.GetPath(this) + "." + name, before, after);
+
+            if (LinkedProperties.Find(this, name, out var link, out var parent))
             {
-                if (LinkedProperties.Find(this, name, out var link, out var parent))
-                {
-                    link.Update(this, parent);
-                }
+                link.Update(this, parent);
             }
         }
     }

+ 15 - 6
InABox.Core/EntityLink.cs

@@ -46,6 +46,16 @@ namespace InABox.Core
 
         public string GetLinkedPath() => _linkedPath;
 
+        protected override IOriginalValues CreateOriginalValues()
+        {
+            return new SubObjectOriginalValues(this);
+        }
+
+        protected override ILoadedColumns CreateLoadedColumns()
+        {
+            return new SubObjectLoadedColumns(this);
+        }
+
         /*
         [Obsolete("Please supply linked Entity")]
         public EntityLink()
@@ -134,15 +144,14 @@ namespace InABox.Core
 
         protected override void DoPropertyChanged(string name, object? before, object? after)
         {
-            if (IsObserving())
+            LinkedProperties.GetParent(this).CascadePropertyChanged(LinkedProperties.GetPath(this) + "." + name, before, after);
+
+            if (LinkedProperties.Find(this, name, out var link, out var parent))
             {
-                if(LinkedProperties.Find(this, name, out var link, out var parent))
-                {
-                    link.Update(this, parent);
-                }
+                link.Update(this, parent);
             }
         }
 
-        public bool IsValid() => !ID.Equals(Guid.Empty) && Deleted.Equals(Guid.Empty);
+        public bool IsValid() => !ID.Equals(Guid.Empty);
     }
 }

+ 4 - 0
InABox.Core/ILookupDefinition.cs

@@ -360,6 +360,10 @@ namespace InABox.Core
             {
                 columns = Columns.Create(TLookup, ColumnTypeFlags.IncludeID | ColumnTypeFlags.IncludeVisible);
             }
+            else
+            {
+                columns.Add(nameof(Entity.ID));
+            }
 
             return columns;
         }

+ 130 - 28
InABox.Core/LinkedProperties.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
@@ -17,30 +18,129 @@ namespace InABox.Core
         void SetLinkedPath(string path);
     }
 
-    // public interface IResult
-    // {
-    //     object? Value { get; set; }
-    //     bool Cancel { get; set; }
-    // }
-    //
-    // public class Result<TType> : IResult
-    // {
-    //     public TType Value { get; set; }
-    //
-    //     object? IResult.Value {
-    //         get => this.Value;
-    //         set => this.Value = (value is TType _type ? _type : default)!;
-    //     }
-    //     
-    //     public bool Cancel { get; set; }
-    //
-    //     public Result(TType value, bool cancel = false)
-    //     {
-    //         Value = value;
-    //         Cancel = cancel;
-    //     }
-    // }
+    public class SubObjectOriginalValues : IOriginalValues
+    {
+        private ISubObject Object { get; set; }
+
+        private IOriginalValues? _rootDictionary;
+        private IOriginalValues RootDictionary
+        {
+            get
+            {
+                _rootDictionary ??= LinkedProperties.GetParent(Object).OriginalValues;
+                return _rootDictionary;
+            }
+        }
+
+        private string? _rootPath;
+        /// <summary>
+        /// Path of this sub object according to the root, suffixed with a "."
+        /// </summary>
+        private string RootPath
+        {
+            get
+            {
+                _rootPath ??= $"{LinkedProperties.GetPath(Object)}.";
+                return _rootPath;
+            }
+        }
+
+        public SubObjectOriginalValues(ISubObject obj)
+        {
+            Object = obj;
+        }
+
+        private string ChangeKey(string key) => $"{RootPath}{key}";
+
+        public bool ContainsKey(string key)
+        {
+            return RootDictionary.ContainsKey(ChangeKey(key));
+        }
+
+        public void Clear()
+        {
+            foreach(var (k, v) in RootDictionary)
+            {
+                if (k.StartsWith(RootPath))
+                {
+                    RootDictionary.Remove(k);
+                }
+            }
+        }
+
+        public void Remove(string key)
+        {
+            RootDictionary.Remove(ChangeKey(key));
+        }
+
+        public bool TryAdd(string key, object? value)
+        {
+            return RootDictionary.TryAdd(ChangeKey(key), value);
+        }
+
+        public bool TryGetValue(string key, out object? value)
+        {
+            return RootDictionary.TryGetValue(ChangeKey(key), out value);
+        }
+
+        public IEnumerator<KeyValuePair<string, object?>> GetEnumerator()
+        {
+            return RootDictionary.Where(x => x.Key.StartsWith(RootPath)).GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+        public object? this[string key]
+        {
+            get => RootDictionary[ChangeKey(key)];
+            set => RootDictionary[ChangeKey(key)] = value;
+        }
+    }
+
+    public class SubObjectLoadedColumns : ILoadedColumns
+    {
+        private ISubObject Object { get; set; }
+
+        private ILoadedColumns? _rootSet;
+        private ILoadedColumns RootSet
+        {
+            get
+            {
+                _rootSet ??= LinkedProperties.GetParent(Object).LoadedColumns;
+                return _rootSet;
+            }
+        }
+
+        private string? _rootPath;
+        /// <summary>
+        /// Path of this sub object according to the root, suffixed with a "."
+        /// </summary>
+        private string RootPath
+        {
+            get
+            {
+                _rootPath ??= $"{LinkedProperties.GetPath(Object)}.";
+                return _rootPath;
+            }
+        }
+
+        public SubObjectLoadedColumns(ISubObject obj)
+        {
+            Object = obj;
+        }
+
+        private string ChangeKey(string key) => $"{RootPath}{key}";
+
+        public bool Add(string key)
+        {
+            return RootSet.Add(ChangeKey(key));
+        }
 
+        public bool Contains(string key)
+        {
+            return RootSet.Contains(ChangeKey(key));
+        }
+    }
 
     public static class LinkedProperties
     {
@@ -71,12 +171,12 @@ namespace InABox.Core
             return filtered;
         }*/
 
-        private static BaseObject GetParent(ISubObject link)
+        public static BaseObject GetParent(ISubObject link)
         {
             while (true)
             {
                 var parent = link.GetLinkedParent();
-                if(parent is IEntityLink parentLink)
+                if(parent is ISubObject parentLink)
                 {
                     link = parentLink;
                 }
@@ -87,13 +187,13 @@ namespace InABox.Core
             }
         }
 
-        private static string GetPath(ISubObject link)
+        public static string GetPath(ISubObject link)
         {
             var path = link.GetLinkedPath();
             while (true)
             {
                 var parent = link.GetLinkedParent();
-                if (parent is IEntityLink parentLink)
+                if (parent is ISubObject parentLink)
                 {
                     link = parentLink;
                     path = $"{link.GetLinkedPath()}.{path}";
@@ -135,7 +235,9 @@ namespace InABox.Core
             if(!_LinkedProperties.TryGetValue(parent.GetType(), out var props))
                 return false;
             var path = GetPath(subObject);
-            property = props.FirstOrDefault(x => String.Equals($"{x.Path}{(String.IsNullOrWhiteSpace(x.Path) ? "" : ".")}{x.Source}", $"{path}{(String.IsNullOrWhiteSpace(path) ? "" : ".")}{name}"));
+            property = props.FirstOrDefault(x => string.Equals(
+                $"{x.Path}{(x.Path.IsNullOrWhiteSpace() ? "" : ".")}{x.Source}",
+                $"{path}{(path.IsNullOrWhiteSpace() ? "" : ".")}{name}"));
             return property != null;
         }
         

+ 0 - 2
inabox.wpf/DynamicGrid/DynamicGridUtils.cs

@@ -372,8 +372,6 @@ public static class DynamicGridUtils
                         var getter = prop.Getter();
                         var oldvalue = getter != null ? getter.Invoke(item) : CoreUtils.GetPropertyValue(item, name);
 
-                        item.OnPropertyChanged(name, oldvalue, value);
-
                         var setter = prop.Setter();
                         if (setter != null && value != null)
                             setter.Invoke(item, value);