| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 | using System;using System.Collections;using System.Collections.Generic;using System.Diagnostics.CodeAnalysis;using System.Linq;using System.Linq.Expressions;namespace InABox.Core{    public interface ISubObject    {        BaseObject? GetLinkedParent();        void SetLinkedParent(BaseObject obj);        string? GetLinkedPath();        void SetLinkedPath(string path);    }    public static class SubObjectExtensions    {        public static bool Synchronise(this ISubObject obj, object Entity)        {            var result = false;            foreach (var prop in DatabaseSchema.Properties(obj.GetType()))            {                if (prop.Name == "ID" || !(prop is StandardProperty stdProp) || stdProp.Property.DeclaringType.Equals(typeof(BaseObject))) continue;                var entityProp = DatabaseSchema.Property(Entity.GetType(), prop.Name);                if(entityProp != null)                {                    prop.Setter()(obj, entityProp.Getter()(Entity));                }            }            foreach (var link in LinkedProperties.Find(obj, out var parent))            {                link.Update(obj, parent);                result = true;            }            return result;        }    }    public class SubObjectOriginalValues : IOriginalValues    {        private ISubObject Object { get; set; }        private IOriginalValues? _rootDictionary;        private IOriginalValues RootDictionary        {            get            {                _rootDictionary ??= (LinkedProperties.GetParent(Object)?.OriginalValueList ?? new 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)).Select(x => new KeyValuePair<string, object?>(x.Key[RootPath.Length..], x.Value)).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 ?? new 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 IEnumerator<string> GetEnumerator()        {            return RootSet.Where(x => x.StartsWith(RootPath)).Select(x => x[RootPath.Length..]).GetEnumerator();        }        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();    }    public static class LinkedProperties    {        private static readonly Dictionary<Type, List<ILinkedProperty>> _LinkedProperties = new Dictionary<Type, List<ILinkedProperty>>();                public static void Register<TLinkedEntity, TEntityLink, TType>(Expression<Func<TLinkedEntity,TEntityLink>> path, Expression<Func<TEntityLink, TType>> source,            Expression<Func<TLinkedEntity, TType>> target, Func<TLinkedEntity,TType, Result<TType, string>>? func = null)            where TLinkedEntity : class            where TEntityLink : class        {            var map = new LinkedProperty<TLinkedEntity, TEntityLink, TType>(path, source, target, func);            if(!_LinkedProperties.TryGetValue(map.Type, out var props))            {                props = new List<ILinkedProperty>();                _LinkedProperties[map.Type] = props;            }            if(!props.Any(x => x.Equals(map)))            {                props.Add(map);            }        }        /*public static IEnumerable<ILinkedProperty> Find(object parent, object path)        {            var all =  _LinkedProperties.Where(x => x.Type == parent.GetType());            var filtered =  all.Where(x => CoreUtils.GetPropertyValue(parent,x.Path)?.GetType() == path?.GetType());            return filtered;        }*/        public static BaseObject? GetParent(ISubObject link)        {            while (true)            {                var parent = link.GetLinkedParent();                if(parent is ISubObject parentLink)                {                    link = parentLink;                }                else                 {                    return parent;                }            }        }        public static string? GetPath(ISubObject link)        {            var path = link.GetLinkedPath();            while (true)            {                var parent = link.GetLinkedParent();                if (parent is ISubObject parentLink)                {                    link = parentLink;                    path = $"{link.GetLinkedPath()}.{path}";                }                else                {                    return path;                }            }        }        public static IEnumerable<ILinkedProperty> FindAll(Type parent)        {            if (_LinkedProperties.TryGetValue(parent, out var props))            {                return props;            }            return Enumerable.Empty<ILinkedProperty>();        }        public static IEnumerable<ILinkedProperty> Find(ISubObject subObject, out BaseObject parent)        {            parent = GetParent(subObject);            if (parent is null || !_LinkedProperties.TryGetValue(parent.GetType(), out var props))            {                return Enumerable.Empty<ILinkedProperty>();            }            var path = GetPath(subObject);            return props.Where(x => x.Path == path);        }        public static bool Find(ISubObject subObject, string name, [NotNullWhen(true)] out ILinkedProperty? property, [NotNullWhen(true)] out BaseObject? parent)        {            property = null;            parent = GetParent(subObject);            if (parent is null)                return false;            if(!_LinkedProperties.TryGetValue(parent.GetType(), out var props))                return false;            var path = GetPath(subObject);            property = props.FirstOrDefault(x => string.Equals(                $"{x.Path}{(x.Path.IsNullOrWhiteSpace() ? "" : ".")}{x.Source}",                $"{path}{(path.IsNullOrWhiteSpace() ? "" : ".")}{name}"));            return property != null;        }            }}
 |