| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 | using System;using System.Collections.Generic;using System.Collections.ObjectModel;using System.Linq;using System.Linq.Expressions;using System.Reflection;using InABox.Clients;using InABox.Core;namespace InABox.Core{    public class Credentials    {        public virtual string UserID { get; set; }        public virtual string Password { get; set; }    }    public interface IEntity    {        Guid ID { get; set; }        Guid Deleted { get; set; }        bool IsChanged();    }    public interface ITaxable    {        double ExTax { get; set; }        double TaxRate { get; set; }        double Tax { get; set; }        double IncTax { get; set; }    }        public interface IIssues    {        string Issues { get; set; }    }    public interface IExportable    {    }    public interface IImportable    {    }    public interface IMergeable    {    }    public interface ISecure { }    public interface IDuplicatable    {        IEntityDuplicator GetDuplicator();    }    public interface IEntityDuplicator    {        //void Duplicate(IFilter filter);        void Duplicate(IEnumerable<BaseObject> entities);    }    public class EntityDuplicator<TEntity> : IEntityDuplicator where TEntity : Entity, IRemotable, IPersistent    {        private readonly List<Tuple<Type, Type, Expression>> _relationships = new List<Tuple<Type, Type, Expression>>();        public void Duplicate(IEnumerable<TEntity> entites) =>            Duplicate(typeof(TEntity),                new Filter<TEntity>(x => x.ID).InList(entites.Select(x => x.ID).ToArray()));        public void Duplicate(IFilter filter)        {            Duplicate(typeof(TEntity), filter);        }        private void Duplicate(Type parent, IFilter filter)        {            var table = ClientFactory.CreateClient(parent).Query(filter);            foreach (var row in table.Rows)            {                var update = (row.ToObject(parent) as Entity)!;                var id = update.ID;                update.ID = Guid.Empty;                update.CommitChanges();                ClientFactory.CreateClient(parent).Save(update, "Duplicated Record");                foreach (var relationship in _relationships.Where(x => x.Item1 == parent))                {                    var filtertype = typeof(Filter<>).MakeGenericType(relationship.Item2);                    var newfilter = Activator.CreateInstance(filtertype) as IFilter;                    filter.Expression = relationship.Item3;                    filter.Operator = Operator.IsEqualTo;                    filter.Value = id;                    Duplicate(relationship.Item2, filter);                }            }        }        public void AddChild<TParent, TChild>(Expression<Func<TChild, IEntityLink<TParent>>> childkey) where TParent : Entity where TChild : Entity        {            _relationships.Add(new Tuple<Type, Type, Expression>(typeof(TParent), typeof(TChild), childkey));        }        void IEntityDuplicator.Duplicate(IEnumerable<BaseObject> entities) => Duplicate(entities.Cast<TEntity>());    }    /// <summary>    /// An <see cref="IProperty"/> is required if it has the <see cref="RequiredColumnAttribute"/> defined on it.<br/>    /// If it is part of an <see cref="IEntityLink"/> (or <see cref="IEnclosedEntity"/>), then it is only required    /// if the <see cref="IEntityLink"/> property on the parent class also has <see cref="RequiredColumnAttribute"/>.    /// </summary>    public class RequiredColumnAttribute : Attribute { }    public abstract class Entity : BaseObject, IEntity    {        private bool bTaxing;        //public String Name { get; set; }        [TimestampEditor(Visible = Visible.Optional, Editable = Editable.Hidden)]        [RequiredColumn]        public virtual DateTime LastUpdate { get; set; }        [CodeEditor(Visible = Visible.Optional, Editable = Editable.Hidden)]        [RequiredColumn]        public string LastUpdateBy { get; set; }        [NullEditor]        [RequiredColumn]        public virtual DateTime Created { get; set; }        [NullEditor]        [RequiredColumn]        public virtual string CreatedBy { get; set; }        [NullEditor]        [RequiredColumn]        public Guid ID { get; set; }        /// <summary>        /// If the entity is deleted, holds the ID of the Deletion. Otherwise, it holds Guid.Empty        /// </summary>        [NullEditor]        [DoNotSerialize]        public Guid Deleted { get; set; }        public static Type ClassVersion(Type t)        {            //Type t = MethodBase.GetCurrentMethod().DeclaringType;            var ti = t.GetTypeInfo();            var interfaces = ti.GetInterfaces();            if (ti.GetInterfaces().Contains(typeof(IPersistent)))            {                if (ti.BaseType != null)                    throw new Exception(t.Name + " hase no Base Type");                if (ti.BaseType.Equals(typeof(Entity)))                    throw new Exception(t.Name + " may not derive directly from TEntity");                var props = t.GetTypeInfo().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);                if (props.Count() > 0)                    throw new Exception(t.Name + "may not declare properties");            }            return t.GetTypeInfo().BaseType;        }        //[NullEditor]        //public List<EntityHistory> History { get; set; }        protected override void Init()        {            base.Init();            ID = Guid.Empty;            Deleted = Guid.Empty;            Created = DateTime.Now;            CreatedBy = ClientFactory.UserID;            LastUpdate = DateTime.Now;            LastUpdateBy = ClientFactory.UserID;            //History = new List<EntityHistory>();                        CheckSequence();        }        //public Entity() : base()        //{        //    CommitChanges();        //}        //public Entity(Guid id) : base()        //{        //    ID = id;        //    History = new List<EntityHistory>();        //    UserProperties = new Dictionary<string, Object>();        //    DataModel.InitializeEntity(this);        //    CheckSequence();        //    CommitChanges();        //}        public static bool IsEntityLinkValid<T, U>(Expression<Func<T, U>> expression, CoreRow arg) where U : IEntityLink        {            return arg.IsEntityLinkValid(expression);        }        /// <summary>        /// Gets the ID of an entity link of an entity, doing a validity check (see <see cref="IsEntityLinkValid{T, U}(Expression{Func{T, U}}, CoreRow)"/>)        /// </summary>        /// <typeparam name="T">The entity type</typeparam>        /// <typeparam name="U">The entity link type</typeparam>        /// <param name="expression">An expression to the entity link of type <typeparamref name="U"/></param>        /// <param name="arg">The row representing the entity of type <typeparamref name="T"/></param>        /// <returns>The ID on the entity link, or <c>null</c> if the entity link is invalid</returns>        public static Guid? EntityLinkID<T, U>(Expression<Func<T, U>> expression, CoreRow arg) where U : IEntityLink        {            var col = CoreUtils.GetFullPropertyName(expression, ".");            var id = arg.Get<Guid>(col + ".ID");            if (id != Guid.Empty && arg.Get<Guid>(col + ".Deleted") == Guid.Empty)                return id;            return null;        }        private void CheckSequence()        {            if (this is ISequenceable seq && seq.Sequence <= 0)            {                seq.Sequence = CoreUtils.GenerateSequence();            }        }        protected override void SetChanged(string name, object before, object after)        {            base.SetChanged(name, before, after);            CheckTax(name, before, after);        }                private void CheckTax(string name, object before, object after)        {            if (this is ITaxable)            {                if (bTaxing)                    return;                bTaxing = true;                try                {                                        var taxable = this as ITaxable;                    if (name.Equals("ExTax"))                    {                        taxable.Tax = (double)after * (taxable.TaxRate / 100.0F);                        taxable.IncTax = (double)after + taxable.Tax;                    }                    else if (name.Equals("TaxRate"))                    {                        taxable.Tax = taxable.ExTax * ((double)after / 100.0F);                        taxable.IncTax = taxable.ExTax + taxable.Tax;                    }                    else if (name.Equals("Tax"))                    {                        taxable.IncTax = (double)after + taxable.Tax;                    }                    else if (name.Equals("IncTax"))                    {                        taxable.ExTax = (double)after / ((100.0F + taxable.TaxRate) / 100.0F);                        taxable.Tax = (double)after - taxable.ExTax;                    }                                }                catch (Exception e)                {                    Logger.Send(LogType.Error, "", String.Join("\n",e.Message,e.StackTrace));                }                                bTaxing = false;            }        }        protected override void DoPropertyChanged(string name, object? before, object? after)        {            if (!IsObserving())                return;            //CheckSequence();            if (!name.Equals("LastUpdate"))                LastUpdate = DateTime.Now;            LastUpdateBy = ClientFactory.UserID;                        // This doesn;t work - keeps being updated to current date            // Created => null ::Set ID = guid.empty -> now :: any other change -> unchanged!            // Moved to Create(), should not simply be overwritten on deserialise from json            //if (Created.Equals(DateTime.MinValue))            //{            //    Created = DateTime.Now;            //    CreatedBy = ClientFactory.UserID;            //}        }            }    public interface ILicense<TLicenseToken> where TLicenseToken : LicenseToken    {    }    public interface IPersistent    {    }    public interface IRemotable    {    }    //public interface IRemoteQuery    //{    //}    //public interface IRemoteUpdate    //{    //}    //public interface IRemoteDelete    //{    //}    public interface ISequenceable    {        long Sequence { get; set; }    }    public interface IAutoIncrement<T, TType>    {        Expression<Func<T, TType>> AutoIncrementField();        Filter<T> AutoIncrementFilter();    }    public interface INumericAutoIncrement<T> : IAutoIncrement<T, int>    {    }    public interface IStringAutoIncrement    {        string AutoIncrementPrefix();        string AutoIncrementFormat();            }        public interface IStringAutoIncrement<T> : IAutoIncrement<T, string>, IStringAutoIncrement    {    }    /// <summary>    /// Used to flag an entity as exhibiting the properties of a ManyToMany relationship, allowing PRS to auto-generate things like grids and datamodels based on    /// entity relationships.    /// </summary>    /// <remarks>    /// This will cause a ManyToMany grid of <typeparamref name="TRight"/> to appear on all <typeparamref name="TLeft"/> editors.    /// Hence, if one wishes to cause both grids to appear (that is, for <typeparamref name="TLeft"/> to appear for <typeparamref name="TRight"/> <i>and</i>    /// vice versa, one must flag the entity with both <c>IManyToMany<<typeparamref name="TLeft"/>, <typeparamref name="TRight"/>></c> and     /// <c>IManyToMany<<typeparamref name="TRight"/>, <typeparamref name="TLeft"/>></c>.    /// </remarks>    /// <typeparam name="TLeft"></typeparam>    /// <typeparam name="TRight"></typeparam>    public interface IManyToMany<TLeft, TRight> where TLeft : Entity where TRight : Entity    {    }    public interface IOneToMany<TOne> where TOne : Entity    {    }    public static class EntityFactory    {        public delegate object ObjectActivator(params object[] args);        private static readonly Dictionary<Type, ObjectActivator> _cache = new Dictionary<Type, ObjectActivator>();        public static ObjectActivator GetActivator<T>(ConstructorInfo ctor)        {            var type = ctor.DeclaringType;            var paramsInfo = ctor.GetParameters();            //create a single param of type object[]            var param =                Expression.Parameter(typeof(object[]), "args");            var argsExp =                new Expression[paramsInfo.Length];            //pick each arg from the params array             //and create a typed expression of them            for (var i = 0; i < paramsInfo.Length; i++)            {                Expression index = Expression.Constant(i);                var paramType = paramsInfo[i].ParameterType;                Expression paramAccessorExp =                    Expression.ArrayIndex(param, index);                Expression paramCastExp =                    Expression.Convert(paramAccessorExp, paramType);                argsExp[i] = paramCastExp;            }            //make a NewExpression that calls the            //ctor with the args we just created            var newExp = Expression.New(ctor, argsExp);            //create a lambda with the New            //Expression as body and our param object[] as arg            var lambda =                Expression.Lambda(typeof(ObjectActivator), newExp, param);            //compile it            var compiled = (ObjectActivator)lambda.Compile();            return compiled;        }        public static T CreateEntity<T>() where T : BaseObject        {            if (!_cache.ContainsKey(typeof(T)))            {                var ctor = typeof(T).GetConstructors().First();                var activator = typeof(EntityFactory).GetMethod("GetActivator").MakeGenericMethod(typeof(T));                _cache[typeof(T)] = GetActivator<T>(ctor);            }            var createdActivator = _cache[typeof(T)];            return (T)createdActivator();        }        public static object CreateEntity(Type type)        {            if (!_cache.ContainsKey(type))            {                var ctor = type.GetConstructors().First();                var activator = typeof(EntityFactory).GetMethod("GetActivator").MakeGenericMethod(type);                _cache[type] = (ObjectActivator)activator.Invoke(null, new object[] { ctor });            }            var createdActivator = _cache[type];            return createdActivator();        }    }}
 |