| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 | using System;using System.Collections.Concurrent;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using System.Linq.Expressions;using System.Reflection;using System.Runtime.Serialization;//using PropertyChanged;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);    }    /// <summary>    ///     Observable object with INotifyPropertyChanged implemented    /// </summary>    public abstract class BaseObject : INotifyPropertyChanged, IBaseObject    {        public BaseObject()        {            SetObserving(false);            Init();            SetObserving(true);        }        [OnDeserializing]        internal void OnDeserializingMethod(StreamingContext context)        {            if (_observing)                SetObserving(false);        }        [OnDeserialized]        internal void OnDeserializedMethod(StreamingContext context)        {            if (!_observing)                SetObserving(true);        }        protected virtual void Init()        {            CheckOriginalValues();            UserProperties = new UserProperties();            //UserProperties.ParentType = this.GetType();            UserProperties.OnPropertyChanged += (o, n, b, a) =>            {                if (IsObserving())                    OnPropertyChanged(n, b, a);            };            DatabaseSchema.InitializeObject(this);        }        #region Observing Flags        public static bool GlobalObserving = true;        private bool _observing = true;        public bool IsObserving()        {            return GlobalObserving && _observing;        }        public void SetObserving(bool active)        {            bApplyingChanges = true;            //UserProperties.ParentType = this.GetType();            _observing = active;            foreach (var oo in CoreUtils.GetChildren<IBaseObject>(this))                oo.SetObserving(active);            /*UserProperties.OnPropertyChanged += (o, n, b, a) =>            {                if (_observing)                    OnPropertyChanged(n, b, a);            };*/            bApplyingChanges = false;        }        protected virtual void DoPropertyChanged(string name, object? before, object? after)        {        }        public event PropertyChangedEventHandler? PropertyChanged;        private bool bApplyingChanges;        private bool bChanged;        [DoNotPersist]        public ConcurrentDictionary<string, object?> OriginalValues { get; set; }        protected virtual void SetChanged(string name, object? before, object? after)        {            bChanged = true;            if (!bApplyingChanges)            {                bApplyingChanges = true;                DoPropertyChanged(name, before, after);                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));                bApplyingChanges = false;            }        }        private bool QueryChanged()        {            CheckOriginalValues();            if (OriginalValues.Count > 0)                return true;            foreach (var oo in CoreUtils.GetChildren<IBaseObject>(this))                if (oo.IsChanged())                    return true;            return false;        }        private 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 void OnPropertyChanged(string name, object? before, object? after)        {            if (!IsObserving())                return;            if (name.Equals("IsChanged"))                return;            if (name.Equals("Observing"))                return;            if (name.Equals("OriginalValues"))                return;            if (!HasChanged(before, after))                return;            CheckOriginalValues();            if (!OriginalValues.ContainsKey(name))                OriginalValues[name] = before;            SetChanged(name, before, after);        }        private void CheckOriginalValues()        {            if (OriginalValues == null)            {                var bObserving = _observing;                _observing = false;                OriginalValues = new ConcurrentDictionary<string, object?>();                _observing = bObserving;            }        }        public bool IsChanged()        {            return IsObserving() ? QueryChanged() : bChanged;        }        public void CancelChanges()        {            bApplyingChanges = true;            var bObs = IsObserving();            SetObserving(false);            CheckOriginalValues();            foreach (var key in OriginalValues.Keys.ToArray())            {                try                {                    var prop = key.Contains(".")                         ? CoreUtils.GetProperty(GetType(),key)                         : GetType().GetRuntimeProperty(key);                    if(prop != null)                    {                        if (prop.SetMethod != null)                        {                            var val = OriginalValues[key];                            // Funky 64bit stuff here?                            if (prop.PropertyType == typeof(int) && val?.GetType() == typeof(long))                                val = Convert.ToInt32(val);                            prop.SetValue(this, val);                        }                    }                    else if (UserProperties.ContainsKey(key))                    {                        UserProperties[key] = OriginalValues[key];                    }                    else                    {                        Logger.Send(LogType.Error, "", $"'{key}' is neither a runtime property nor custom property of {GetType().Name}");                    }                }                catch (Exception e)                {                    Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));                }            }            OriginalValues.Clear();            bChanged = false;            foreach (var oo in CoreUtils.GetChildren<IBaseObject>(this)) oo.CancelChanges();            SetObserving(bObs);            bApplyingChanges = false;        }        public void CommitChanges()        {            bApplyingChanges = true;            CheckOriginalValues();            OriginalValues.Clear();            bChanged = false;            foreach (var oo in CoreUtils.GetChildren<IBaseObject>(this))                oo.CommitChanges();            bApplyingChanges = false;        }        public string ChangedValues()        {            var result = new List<string>();            var type = GetType();            try            {                CheckOriginalValues();                foreach (var key in OriginalValues.Keys)                    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;        public UserProperties UserProperties        {            get            {                CheckUserProperties();                return _userproperties;            }            set            {                _userproperties = value;                CheckUserProperties();            }        }        private static Dictionary<string, object?>? DefaultProperties;        private void CheckUserProperties()        {            if (_userproperties == null)            {                _userproperties = new UserProperties();                if (DefaultProperties == null)                {                    DefaultProperties = new Dictionary<string, object?>();                    var props = DatabaseSchema.Properties(GetType()).Where(x => x is CustomProperty).ToArray();                    foreach (var field in props)                        DefaultProperties[field.Name] = DatabaseSchema.DefaultValue(field.PropertyType);                }                _userproperties.LoadFromDictionary(DefaultProperties);            }        }        #endregion    }    public static class BaseObjectExtensions    {        public static bool HasOriginalValue<T>(this T sender, string propertyname) where T : BaseObject        {            return sender.OriginalValues != null && sender.OriginalValues.ContainsKey(propertyname);        }        public static TType GetOriginalValue<T, TType>(this T sender, string propertyname) where T : BaseObject        {            return sender.OriginalValues != null && sender.OriginalValues.ContainsKey(propertyname)                ? (TType)CoreUtils.ChangeType(sender.OriginalValues[propertyname], typeof(TType))                : default;        }        public static Dictionary<string, object> GetValues<T>(this T sender, bool all) where T : BaseObject        {            var result = new Dictionary<string, object>();            LoadValues(sender, result, "", all, false);            return result;        }        public static Dictionary<string, object> GetOriginaValues<T>(this T sender, bool all) where T : BaseObject        {            var result = new Dictionary<string, object>();            LoadValues(sender, result, "", all, true);            return result;        }        private static void LoadValues(BaseObject sender, Dictionary<string, object?> values, string prefix, bool all, bool original)        {            try            {                var props = sender.GetType().GetProperties().Where(x =>                    x.GetCustomAttribute<DoNotSerialize>() == null                    && x.GetCustomAttribute<DoNotPersist>() == null                    && x.GetCustomAttribute<AggregateAttribute>() == null                    && x.GetCustomAttribute<FormulaAttribute>() == null                    && x.GetCustomAttribute<ConditionAttribute>() == null                    && x.CanWrite);                foreach (var prop in props)                    if (prop.PropertyType.GetInterfaces().Contains(typeof(IEnclosedEntity)))                    {                        var child = prop.GetValue(sender) as BaseObject;                        if (child != null)                            LoadValues(child, values, string.IsNullOrWhiteSpace(prefix) ? prop.Name : prefix + "." + prop.Name, all, original);                    }                    else if (prop.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)))                    {                        var child = prop.GetValue(sender) as BaseObject;                        if (child != null)                        {                            if (all)                            {                                values[string.IsNullOrWhiteSpace(prefix) ? prop.Name + ".ID" : prefix + "." + prop.Name + ".ID"] =                                    original                                        ? child.OriginalValues.ContainsKey("ID") ? child.OriginalValues["ID"] : Guid.Empty                                        : CoreUtils.GetPropertyValue(child, "ID");                            }                            else                            {                                if (child.HasOriginalValue("ID"))                                    values[string.IsNullOrWhiteSpace(prefix) ? prop.Name + ".ID" : prefix + "." + prop.Name + ".ID"] =                                        original                                            ? child.OriginalValues.ContainsKey("ID") ? child.OriginalValues["ID"] : Guid.Empty                                            : CoreUtils.GetPropertyValue(child, "ID");                            }                        }                    }                    else if (prop.PropertyType.GetInterfaces().Contains(typeof(IBaseObject)))                    {                        var child = prop.GetValue(sender) as IBaseObject;                        if (child != null)                        {                            if (all)                            {                                values[string.IsNullOrWhiteSpace(prefix) ? prop.Name : prefix + "." + prop.Name] = child;                            }                            else                            {                                if (child.IsChanged())                                    values[string.IsNullOrWhiteSpace(prefix) ? prop.Name : prefix + "." + prop.Name] = child;                            }                        }                    }                    else                    {                        if (all)                        {                            values[string.IsNullOrWhiteSpace(prefix) ? prop.Name : prefix + "." + prop.Name] =                                original                                    ? sender.OriginalValues.ContainsKey(prop.Name) ? sender.OriginalValues[prop.Name] : prop.PropertyType.GetDefault()                                    : prop.GetValue(sender);                        }                        else                        {                            if (sender.HasOriginalValue(prop.Name))                                values[string.IsNullOrWhiteSpace(prefix) ? prop.Name : prefix + "." + prop.Name] =                                    original                                        ? sender.OriginalValues.ContainsKey(prop.Name)                                            ? sender.OriginalValues[prop.Name]                                            : prop.PropertyType.GetDefault()                                        : prop.GetValue(sender);                        }                    }                var iprops = DatabaseSchema.Properties(sender.GetType()).Where(x => x is CustomProperty);                foreach (var iprop in iprops)                    if (all || sender.HasOriginalValue(iprop.Name))                        values[string.IsNullOrWhiteSpace(prefix) ? iprop.Name : prefix + "." + iprop.Name] =                            original                                ? sender.OriginalValues.ContainsKey(iprop.Name)                                    ? sender.OriginalValues[iprop.Name]                                    : iprop.PropertyType.GetDefault()                                : sender.UserProperties[iprop.Name];            }            catch (Exception e)            {                Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));            }        }                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.OriginalValues[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.OriginalValues != null && sender.OriginalValues.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.OriginalValues != null && sender.OriginalValues.ContainsKey(prop.Name)                ? (TType)CoreUtils.ChangeType(sender.OriginalValues[prop.Name], typeof(TType))                : default;        }        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.OriginalValues[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; }    }}
 |