123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- 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 IEnclosedEntity
- {
- }
-
- 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)
- {
- var seq = (ISequenceable)this;
- if (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;
- 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;
- }
- 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;
- //}
- }
- #region Linked Properties
- // Why?
- [DoNotSerialize]
- private static readonly List<LinkedProperty> _LinkedProperties = new List<LinkedProperty>();
- public virtual void LinkProperty<TEntityLink, TLinkedEntity>(Expression<Func<TEntityLink, object>> source,
- Expression<Func<TLinkedEntity, object>> target)
- {
- var map = LinkedProperty.Create(source, target);
- if (!_LinkedProperties.Any(x =>
- x.Source.DeclaringType.Equals(typeof(TEntityLink)) && x.Source.Name.Equals(map.Source.Name) &&
- x.Source.Name.Equals(map.Source.Name) && x.Target.DeclaringType.Equals(typeof(TLinkedEntity)) &&
- x.Target.Name.Equals(map.Target.Name)))
- _LinkedProperties.Add(LinkedProperty.Create(source, target));
- }
- public IEnumerable<LinkedProperty> LinkedProperties(Type type)
- {
- var result = _LinkedProperties.Where(x =>
- (x.Source.DeclaringType.Equals(type) || x.Source.DeclaringType.IsSubclassOf(type)) &&
- (x.Target.DeclaringType.Equals(GetType()) || x.Target.DeclaringType.IsSubclassOf(GetType()))
- );
- return result;
- }
- #endregion
- }
- 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<T> : IAutoIncrement<T, string>
- {
- string AutoIncrementFormat();
- }
- /// <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();
- }
- }
- }
|