| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707 | 
							- using AutoProperties;
 
- using Newtonsoft.Json;
 
- using System;
 
- using System.Collections;
 
- using System.Collections.Concurrent;
 
- using System.Collections.Generic;
 
- using System.ComponentModel;
 
- using System.Data;
 
- using System.Diagnostics.CodeAnalysis;
 
- using System.Linq;
 
- using System.Linq.Expressions;
 
- using System.Reflection;
 
- using System.Runtime.CompilerServices;
 
- using System.Runtime.Serialization;
 
- namespace InABox.Core
 
- {
 
-     public class DoNotSerialize : Attribute
 
-     {
 
-     }
 
-     public interface IBaseObject
 
-     {
 
-         public bool IsChanged();
 
-         public void CancelChanges();
 
-         public void CommitChanges();
 
-         public bool IsObserving();
 
-         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
 
-     {
 
-         public OriginalValues()
 
-         {
 
-         }
 
-         public ConcurrentDictionary<string, object?> Dictionary { get; set; } = 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 : IEnumerable<string>
 
-     {
 
-         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);
 
-         }
 
-         public IEnumerator<string> GetEnumerator()
 
-         {
 
-             return Columns.GetEnumerator();
 
-         }
 
-         IEnumerator IEnumerable.GetEnumerator()
 
-         {
 
-             return Columns.GetEnumerator();
 
-         }
 
-     }
 
-     /// <summary>
 
-     ///     Observable object with INotifyPropertyChanged implemented
 
-     /// </summary>
 
-     public abstract class BaseObject : INotifyPropertyChanged, IBaseObject
 
-     {
 
-         public BaseObject()
 
-         {
 
-             SetObserving(false);
 
-             Init();
 
-             SetObserving(true);
 
-         }
 
-         private bool _disabledInterceptor;
 
-         protected T InitializeField<T>(ref T? field, string name)
 
-             where T : BaseObject, ISubObject, new()
 
-         {
 
-             if(field is null)
 
-             {
 
-                 var value = new T();
 
-                 value.SetLinkedParent(this);
 
-                 value.SetLinkedPath(name);
 
-                 value.SetObserving(_observing);
 
-                 field = value;
 
-             }
 
-             return field;
 
-         }
 
-         [GetInterceptor]
 
-         protected T GetValue<T>(Type propertyType, ref T field, string name)
 
-         {
 
-             if (_disabledInterceptor) return field;
 
-             if(field is null && propertyType.HasInterface<ISubObject>() && !propertyType.IsAbstract)
 
-             {
 
-                 var value = Activator.CreateInstance<T>();
 
-                 var subObj = (value as ISubObject)!;
 
-                 subObj.SetLinkedParent(this);
 
-                 subObj.SetLinkedPath(name);
 
-                 if(subObj is BaseObject obj)
 
-                 {
 
-                     obj.SetObserving(_observing);
 
-                 }
 
-                 field = value;
 
-             }
 
-             return field;
 
-         }
 
-         [SetInterceptor]
 
-         protected void SetValue<T>(ref T field, T newValue)
 
-         {
 
-             field = newValue;
 
-         }
 
-         [OnDeserializing]
 
-         internal void OnDeserializingMethod(StreamingContext context)
 
-         {
 
-             if (_observing)
 
-                 SetObserving(false);
 
-         }
 
-         [OnDeserialized]
 
-         internal void OnDeserializedMethod(StreamingContext context)
 
-         {
 
-             if (!_observing)
 
-                 SetObserving(true);
 
-         }
 
-         protected virtual void Init()
 
-         {
 
-             LoadedColumns = CreateLoadedColumns();
 
-             
 
-             CheckSequence();
 
-         }
 
-         private void CheckSequence()
 
-         {
 
-             if (this is ISequenceable seq && seq.Sequence <= 0)
 
-             {
 
-                 seq.Sequence = CoreUtils.GenerateSequence();
 
-             }
 
-         }
 
-         #region Observing Flags
 
-         public static bool GlobalObserving = true;
 
-         private bool _observing = false;
 
-         public bool IsObserving()
 
-         {
 
-             return GlobalObserving && _observing;
 
-         }
 
-         public void SetObserving(bool active)
 
-         {
 
-             bApplyingChanges = true;
 
-             _observing = active;
 
-             _disabledInterceptor = true;
 
-             foreach (var oo in DatabaseSchema.GetSubObjects(this))
 
-                 oo.SetObserving(active);
 
-             _disabledInterceptor = false;
 
-             bApplyingChanges = false;
 
-         }
 
-         protected virtual void DoPropertyChanged(string name, object? before, object? after)
 
-         {
 
-         }
 
-         public event PropertyChangedEventHandler? PropertyChanged;
 
-         private bool bApplyingChanges;
 
-         private bool bChanged;
 
-         private IOriginalValues? _originalValues;
 
-         [DoNotPersist]
 
-         [DoNotSerialize]
 
-         public IOriginalValues OriginalValueList
 
-         {
 
-             get
 
-             {
 
-                 _originalValues ??= CreateOriginalValues();
 
-                 return _originalValues;
 
-             }
 
-         }
 
-         [DoNotPersist]
 
-         public ConcurrentDictionary<string, object?>? OriginalValues
 
-         {
 
-             get
 
-             {
 
-                 if(OriginalValueList is OriginalValues v)
 
-                 {
 
-                     return v.Dictionary;
 
-                 }
 
-                 else
 
-                 {
 
-                     return null;
 
-                 }
 
-             }
 
-             set
 
-             {
 
-                 if(value != null && OriginalValueList is OriginalValues v)
 
-                 {
 
-                     v.Dictionary = value;
 
-                 }
 
-             }
 
-         }
 
-         [DoNotPersist]
 
-         [DoNotSerialize]
 
-         [JsonIgnore]
 
-         public ILoadedColumns LoadedColumns { get; set; }
 
-         protected virtual void SetChanged(string name, object? before, object? after)
 
-         {
 
-             bChanged = true;
 
-             if (!bApplyingChanges)
 
-             {
 
-                 try
 
-                 {
 
-                     bApplyingChanges = true;
 
-                     DoPropertyChanged(name, before, after);
 
-                     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
 
-                 }
 
-                 catch (Exception)
 
-                 {
 
-                     
 
-                 }
 
-                 bApplyingChanges = false;
 
-             }
 
-         }
 
-         // 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()
 
-         {
 
-             if (OriginalValueList.Any())
 
-                 return true;
 
-             _disabledInterceptor = true;
 
-             foreach (var oo in DatabaseSchema.GetSubObjects(this))
 
-                 if (oo.IsChanged())
 
-                 {
 
-                     _disabledInterceptor = false;
 
-                     return true;
 
-                 }
 
-             _disabledInterceptor = false;
 
-             return false;
 
-         }
 
-         public void OnPropertyChanged(string name, object? before, object? after)
 
-         {
 
-             if (!IsObserving())
 
-                 return;
 
-             if (name.Equals("IsChanged") || name.Equals("Observing") || name.Equals("OriginalValues"))
 
-                 return;
 
-             LoadedColumns.Add(name);
 
-             if (!BaseObjectExtensions.HasChanged(before, after))
 
-                 return;
 
-             if (!OriginalValueList.ContainsKey(name))
 
-                 OriginalValueList[name] = before;
 
-             SetChanged(name, before, after);
 
-         }
 
-         protected virtual IOriginalValues CreateOriginalValues()
 
-         {
 
-             return new OriginalValues();
 
-         }
 
-         protected virtual ILoadedColumns CreateLoadedColumns()
 
-         {
 
-             return new LoadedColumns();
 
-         }
 
-         public bool IsChanged()
 
-         {
 
-             return IsObserving() ? QueryChanged() : bChanged;
 
-         }
 
-         public void CancelChanges()
 
-         {
 
-             bApplyingChanges = true;
 
-             var bObs = IsObserving();
 
-             SetObserving(false);
 
-             foreach (var (key, value) in OriginalValueList)
 
-             {
 
-                 try
 
-                 {
 
-                     var prop = DatabaseSchema.Property(GetType(), key);
 
-                     if(prop != null)
 
-                     {
 
-                         prop.Setter()(this, value);
 
-                     }
 
-                     else
 
-                     {
 
-                         Logger.Send(LogType.Error, "", $"'{key}' is not a property of {GetType().Name}");
 
-                     }
 
-                 }
 
-                 catch (Exception e)
 
-                 {
 
-                     Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
 
-                 }
 
-             }
 
-             OriginalValueList.Clear();
 
-             bChanged = false;
 
-             _disabledInterceptor = true;
 
-             foreach (var oo in DatabaseSchema.GetSubObjects(this))
 
-             {
 
-                 oo.CancelChanges();
 
-             }
 
-             _disabledInterceptor = false;
 
-             SetObserving(bObs);
 
-             bApplyingChanges = false;
 
-         }
 
-         public void CommitChanges()
 
-         {
 
-             bApplyingChanges = true;
 
-             OriginalValueList.Clear();
 
-             bChanged = false;
 
-             _disabledInterceptor = true;
 
-             foreach (var oo in DatabaseSchema.GetSubObjects(this))
 
-                 oo.CommitChanges();
 
-             _disabledInterceptor = false;
 
-             bApplyingChanges = false;
 
-         }
 
-         public string ChangedValues()
 
-         {
 
-             var result = new List<string>();
 
-             var type = GetType();
 
-             try
 
-             {
 
-                 foreach (var (key, _) in OriginalValueList)
 
-                     try
 
-                     {
 
-                         if (UserProperties.ContainsKey(key))
 
-                         {
 
-                             var obj = UserProperties[key];
 
-                             result.Add(string.Format("[{0} = {1}]", key, obj != null ? obj.ToString() : "null"));
 
-                         }
 
-                         else
 
-                         {
 
-                             var prop = DatabaseSchema.Property(type, key);//  GetType().GetProperty(key);
 
-                             if (prop is StandardProperty standard && standard.Loggable != null)
 
-                             {
 
-                                 /*var attribute = //prop.GetCustomAttributes(typeof(LoggablePropertyAttribute), true).FirstOrDefault();
 
-                                 if (attribute != null)
 
-                                 {*/
 
-                                 //var lpa = (LoggablePropertyAttribute)attribute;
 
-                                 var format = standard.Loggable.Format;
 
-                                 var value = standard.Getter()(this);
 
-                                 if (string.IsNullOrEmpty(format))
 
-                                     result.Add($"[{key} = {value}]");
 
-                                 else
 
-                                     result.Add(string.Format("[{0} = {1:" + format + "}]", key, value));
 
-                                 //}
 
-                             }
 
-                         }
 
-                     }
 
-                     catch (Exception e)
 
-                     {
 
-                         Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
 
-                     }
 
-             }
 
-             catch (Exception e)
 
-             {
 
-                 Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
 
-             }
 
-             return string.Join(" ", result);
 
-         }
 
-         #endregion
 
-         #region UserProperties
 
-         private UserProperties? _userproperties;
 
-         private static readonly Dictionary<Type, Dictionary<string, object?>> DefaultProperties = new Dictionary<Type, Dictionary<string, object?>>();
 
-         [DoNotPersist]
 
-         public UserProperties UserProperties
 
-         {
 
-             get
 
-             {
 
-                 if (_userproperties == null)
 
-                 {
 
-                     _userproperties = new UserProperties();
 
-                     var type = GetType();
 
-                     if (!DefaultProperties.TryGetValue(type, out var defaultProps))
 
-                     {
 
-                         defaultProps = new Dictionary<string, object?>();
 
-                         var props = DatabaseSchema.Properties(type).Where(x => x is CustomProperty);
 
-                         foreach (var field in props)
 
-                             defaultProps[field.Name] = DatabaseSchema.DefaultValue(field.PropertyType);
 
-                         DefaultProperties[type] = defaultProps;
 
-                     }
 
-                     _userproperties.LoadFromDictionary(defaultProps);
 
-                     _userproperties.OnPropertyChanged += (o, n, b, a) =>
 
-                     {
 
-                         if (IsObserving())
 
-                             OnPropertyChanged(n, b, a);
 
-                     };
 
-                 }
 
-                 return _userproperties;
 
-             }
 
-         }
 
-         #endregion
 
-     }
 
-     public class BaseObjectSnapshot<T>
 
-         where T : BaseObject
 
-     {
 
-         private readonly List<(IProperty, object?)> Values;
 
-         private readonly T Object;
 
-         public BaseObjectSnapshot(T obj)
 
-         {
 
-             Values = new List<(IProperty, object?)>();
 
-             foreach(var property in DatabaseSchema.Properties(obj.GetType()))
 
-             {
 
-                 Values.Add((property, property.Getter()(obj)));
 
-             }
 
-             Object = obj;
 
-         }
 
-         public void ResetObject()
 
-         {
 
-             Object.CancelChanges();
 
-             var bObs = Object.IsObserving();
 
-             Object.SetObserving(false);
 
-             foreach(var (prop, value) in Values)
 
-             {
 
-                 var oldValue = prop.Getter()(Object);
 
-                 prop.Setter()(Object, value);
 
-                 if(BaseObjectExtensions.HasChanged(oldValue, value))
 
-                 {
 
-                     Object.OriginalValueList[prop.Name] = oldValue;
 
-                 }
 
-             }
 
-             Object.SetObserving(bObs);
 
-         }
 
-     }
 
-     public static class BaseObjectExtensions
 
-     {
 
-         public static bool HasChanged(object? before, object? after)
 
-         {
 
-             if ((before == null || before.Equals("")) && (after == null || after.Equals("")))
 
-                 return false;
 
-             if (before == null != (after == null))
 
-                 return true;
 
-             if (!before!.GetType().Equals(after!.GetType()))
 
-                 return true;
 
-             if (before is string[] && after is string[])
 
-                 return !(before as string[]).SequenceEqual(after as string[]);
 
-             return !before.Equals(after);
 
-         }
 
-         public static bool HasColumn<T>(this T sender, string column)
 
-             where T : BaseObject
 
-         {
 
-             return sender.LoadedColumns.Contains(column);
 
-         }
 
-         public static bool HasColumn<T, TType>(this T sender, Expression<Func<T, TType>> column)
 
-             where T : BaseObject
 
-         {
 
-             return sender.LoadedColumns.Contains(CoreUtils.GetFullPropertyName(column, "."));
 
-         }
 
-         public static bool HasOriginalValue<T>(this T sender, string propertyname) where T : BaseObject
 
-         {
 
-             return sender.OriginalValueList != null && sender.OriginalValueList.ContainsKey(propertyname);
 
-         }
 
-         public static TType GetOriginalValue<T, TType>(this T sender, string propertyname) where T : BaseObject
 
-         {
 
-             return sender.OriginalValueList != null && sender.OriginalValueList.ContainsKey(propertyname)
 
-                 ? (TType)CoreUtils.ChangeType(sender.OriginalValueList[propertyname], typeof(TType))
 
-                 : default;
 
-         }
 
-         /// <summary>
 
-         ///     Get all database values (i.e., non-calculated, local properties) for a given object <paramref name="sender"/>.
 
-         ///     If <paramref name="all"/> is <see langword="false"/>, only retrieve values which have changed.
 
-         /// </summary>
 
-         public static Dictionary<string, object?> GetValues<T>(this T sender, bool all) where T : BaseObject
 
-         {
 
-             var result = new Dictionary<string, object?>();
 
-             foreach(var property in DatabaseSchema.Properties(sender.GetType()))
 
-             {
 
-                 if (property.IsDBColumn && (all || sender.HasOriginalValue(property.Name)))
 
-                 {
 
-                     result[property.Name] = property.Getter()(sender);
 
-                 }
 
-             }
 
-             return result;
 
-         }
 
-         public static Dictionary<string, object?> GetOriginaValues<T>(this T sender) where T : BaseObject
 
-         {
 
-             var result = new Dictionary<string, object?>();
 
-             foreach(var property in DatabaseSchema.Properties(sender.GetType()))
 
-             {
 
-                 if (property.IsDBColumn && sender.OriginalValueList.TryGetValue(property.Name, out var value))
 
-                 {
 
-                     result[property.Name] = value;
 
-                 }
 
-             }
 
-             return result;
 
-         }
 
-         public static BaseObjectSnapshot<T> TakeSnapshot<T>(this T obj)
 
-             where T : BaseObject
 
-         {
 
-             return new BaseObjectSnapshot<T>(obj);
 
-         }
 
-         public static List<string> Compare<T>(this T sender, Dictionary<string, object> original) where T : BaseObject
 
-         {
 
-             var result = new List<string>();
 
-             var current = GetValues(sender, true);
 
-             foreach (var key in current.Keys)
 
-                 if (original.ContainsKey(key))
 
-                 {
 
-                     if (current[key] == null)
 
-                     {
 
-                         if (original[key] != null)
 
-                             result.Add(string.Format("[{0}] has changed from [{1}] to [{2}]", key, original[key], current[key]));
 
-                     }
 
-                     else
 
-                     {
 
-                         if (!current[key].Equals(original[key]))
 
-                             result.Add(string.Format("[{0}] has changed from [{1}] to [{2}]", key, original[key], current[key]));
 
-                     }
 
-                 }
 
-                 else
 
-                 {
 
-                     result.Add(string.Format("[{0}] not present in previous dictionary!", key));
 
-                 }
 
-             return result;
 
-         }
 
-         public static bool GetValue<T,TType>(this T sender, Expression<Func<T,TType>> property, bool original, out TType result) where T : BaseObject
 
-         {
 
-             if (sender.HasOriginalValue<T, TType>(property))
 
-             {
 
-                 if (original)
 
-                     result = sender.GetOriginalValue<T, TType>(property);
 
-                 else
 
-                 {
 
-                     var expr = property.Compile();
 
-                     result = expr(sender);                    
 
-                 }
 
-                 return true;
 
-             }
 
-             result = default(TType);
 
-             return false;
 
-         }
 
-         
 
-         public static void SetOriginalValue<T, TType>(this T sender, string propertyname, TType value) where T : BaseObject
 
-         {
 
-             sender.OriginalValueList[propertyname] = value;
 
-         }
 
-         public static bool HasOriginalValue<T, TType>(this T sender, Expression<Func<T, TType>> property) where T : BaseObject
 
-         {
 
-             //var prop = ((MemberExpression)property.Body).Member as PropertyInfo;
 
-             String propname = CoreUtils.GetFullPropertyName(property, ".");
 
-             return !String.IsNullOrWhiteSpace(propname) && sender.OriginalValueList != null && sender.OriginalValueList.ContainsKey(propname);
 
-         }
 
-         public static TType GetOriginalValue<T, TType>(this T sender, Expression<Func<T, TType>> property) where T : BaseObject
 
-         {
 
-             var prop = ((MemberExpression)property.Body).Member as PropertyInfo;
 
-             return prop != null && sender.OriginalValueList != null && sender.OriginalValueList.ContainsKey(prop.Name)
 
-                 ? (TType)CoreUtils.ChangeType(sender.OriginalValueList[prop.Name], typeof(TType))
 
-                 : default;
 
-         }
 
-         
 
-         public static TType GetOriginalValue<T, TType>(this T sender, Expression<Func<T, TType>> property, TType defaultValue) where T : BaseObject
 
-         {
 
-             var prop = ((MemberExpression)property.Body).Member as PropertyInfo;
 
-             return prop != null && sender.OriginalValueList != null && sender.OriginalValueList.ContainsKey(prop.Name)
 
-                 ? (TType)CoreUtils.ChangeType(sender.OriginalValueList[prop.Name], typeof(TType))
 
-                 : defaultValue;
 
-         }
 
-         public static void SetOriginalValue<T, TType>(this T sender, Expression<Func<T, TType>> property, TType value) where T : BaseObject
 
-         {
 
-             var prop = ((MemberExpression)property.Body).Member as PropertyInfo;
 
-             sender.OriginalValueList[prop.Name] = value;
 
-         }
 
-     }
 
-     /// <summary>
 
-     /// An <see cref="IProperty"/> is loggable if it has the <see cref="LoggablePropertyAttribute"/> defined on it.<br/>
 
-     /// If it is part of an <see cref="IEntityLink"/>, then it is only loggable if the <see cref="IEntityLink"/> property on the parent class
 
-     /// also has <see cref="LoggablePropertyAttribute"/>.
 
-     /// </summary>
 
-     public class LoggablePropertyAttribute : Attribute
 
-     {
 
-         public string Format { get; set; }
 
-     }
 
- }
 
 
  |