Entity.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Reflection;
  6. using InABox.Clients;
  7. using InABox.Core;
  8. namespace InABox.Core
  9. {
  10. public class Credentials
  11. {
  12. public virtual string UserID { get; set; }
  13. public virtual string Password { get; set; }
  14. }
  15. public interface IEnclosedEntity
  16. {
  17. }
  18. //[JsonObject]
  19. //public class EntityHistory : BaseObject
  20. //{
  21. // public DateTime Timestamp { get; set; }
  22. // public String User { get; set; }
  23. // public String Note { get; set; }
  24. //}
  25. public class AutoEntity : Attribute
  26. {
  27. public IAutoEntityGenerator? Generator { get; private set; }
  28. public AutoEntity(Type generator) : base()
  29. {
  30. Generator = Activator.CreateInstance(generator) as IAutoEntityGenerator;
  31. }
  32. }
  33. public interface IAutoEntityGenerator
  34. {
  35. bool Distinct { get; }
  36. Type Definition { get; }
  37. }
  38. public interface IAutoEntityUnionGenerator : IAutoEntityGenerator
  39. {
  40. Type[] Entities { get; }
  41. }
  42. public abstract class AutoEntityUnionGenerator<TInterface> : IAutoEntityUnionGenerator
  43. {
  44. public AutoEntityUnionGenerator()
  45. {
  46. Configure();
  47. }
  48. private List<Type> _entities = new List<Type>();
  49. public void Add<TType>()
  50. where TType : TInterface
  51. {
  52. _entities.Add(typeof(TType));
  53. }
  54. protected abstract void Configure();
  55. public Type[] Entities => _entities.ToArray();
  56. public Type Definition => typeof(TInterface);
  57. public abstract bool Distinct { get; }
  58. }
  59. public interface IAutoEntityCrossGenerator : IAutoEntityGenerator
  60. {
  61. String LeftEntity();
  62. String LeftProperty();
  63. String LeftMapping();
  64. String LeftLink();
  65. String RightEntity();
  66. String RightProperty();
  67. String RightMapping();
  68. String RightLink();
  69. }
  70. public abstract class AutoEntityCrossGenerator<TInterface, TLeft, TRight> : IAutoEntityCrossGenerator
  71. {
  72. public abstract Expression<Func<TLeft, Guid>> LeftProperty { get; }
  73. public abstract Expression<Func<TInterface, Guid>> LeftMapping { get; }
  74. public abstract Expression<Func<TLeft, Guid>> LeftLink { get; }
  75. public abstract Expression<Func<TRight, Guid>> RightProperty { get; }
  76. public abstract Expression<Func<TInterface, Guid>> RightMapping { get; }
  77. public abstract Expression<Func<TRight, Guid>> RightLink { get; }
  78. public string LeftEntity() => typeof(TLeft).EntityName().Split('.').Last();
  79. string IAutoEntityCrossGenerator.LeftProperty() => CoreUtils.GetFullPropertyName(LeftProperty, ".");
  80. string IAutoEntityCrossGenerator.LeftMapping() => CoreUtils.GetFullPropertyName(LeftMapping, ".");
  81. string IAutoEntityCrossGenerator.LeftLink() => CoreUtils.GetFullPropertyName(LeftLink, ".");
  82. public string RightEntity() => typeof(TRight).EntityName().Split('.').Last();
  83. string IAutoEntityCrossGenerator.RightProperty() => CoreUtils.GetFullPropertyName(RightProperty, ".");
  84. string IAutoEntityCrossGenerator.RightMapping() => CoreUtils.GetFullPropertyName(RightMapping, ".");
  85. string IAutoEntityCrossGenerator.RightLink() => CoreUtils.GetFullPropertyName(RightLink, ".");
  86. public abstract bool Distinct { get; }
  87. public Type Definition => typeof(TInterface);
  88. }
  89. public interface IEntity
  90. {
  91. Guid ID { get; set; }
  92. Guid Deleted { get; set; }
  93. bool IsChanged();
  94. }
  95. public interface ITaxable
  96. {
  97. double ExTax { get; set; }
  98. double TaxRate { get; set; }
  99. double Tax { get; set; }
  100. double IncTax { get; set; }
  101. }
  102. public interface IIssues
  103. {
  104. string Issues { get; set; }
  105. }
  106. public interface IExportable
  107. {
  108. }
  109. public interface IImportable
  110. {
  111. }
  112. public interface IMergeable
  113. {
  114. }
  115. public interface ISecure { }
  116. public interface IDuplicatable
  117. {
  118. IEntityDuplicator GetDuplicator();
  119. }
  120. public interface IEntityDuplicator
  121. {
  122. void Duplicate(IFilter filter);
  123. }
  124. public class EntityDuplicator<TEntity> : IEntityDuplicator where TEntity : Entity, IRemotable, IPersistent
  125. {
  126. private readonly List<Tuple<Type, Type, Expression>> _relationships = new List<Tuple<Type, Type, Expression>>();
  127. public void Duplicate(IFilter filter)
  128. {
  129. Duplicate(typeof(TEntity), filter);
  130. }
  131. private void Duplicate(Type parent, IFilter filter)
  132. {
  133. var table = ClientFactory.CreateClient(parent).Query(filter);
  134. foreach (var row in table.Rows)
  135. {
  136. var update = (row.ToObject(parent) as Entity)!;
  137. var id = update.ID;
  138. update.ID = Guid.Empty;
  139. update.CommitChanges();
  140. ClientFactory.CreateClient(parent).Save(update, "Duplicated Record");
  141. foreach (var relationship in _relationships.Where(x => x.Item1 == parent))
  142. {
  143. var filtertype = typeof(Filter<>).MakeGenericType(relationship.Item2);
  144. var newfilter = Activator.CreateInstance(filtertype) as IFilter;
  145. filter.Expression = relationship.Item3;
  146. filter.Operator = Operator.IsEqualTo;
  147. filter.Value = id;
  148. Duplicate(relationship.Item2, filter);
  149. }
  150. }
  151. }
  152. public void AddChild<TParent, TChild>(Expression<Func<TChild, IEntityLink<TParent>>> childkey) where TParent : Entity where TChild : Entity
  153. {
  154. _relationships.Add(new Tuple<Type, Type, Expression>(typeof(TParent), typeof(TChild), childkey));
  155. }
  156. }
  157. /// <summary>
  158. /// An <see cref="IProperty"/> is required if it has the <see cref="RequiredColumnAttribute"/> defined on it.<br/>
  159. /// If it is part of an <see cref="IEntityLink"/> (or <see cref="IEnclosedEntity"/>), then it is only required
  160. /// if the <see cref="IEntityLink"/> property on the parent class also has <see cref="RequiredColumnAttribute"/>.
  161. /// </summary>
  162. public class RequiredColumnAttribute : Attribute { }
  163. public abstract class Entity : BaseObject, IEntity
  164. {
  165. private bool bTaxing;
  166. //public String Name { get; set; }
  167. [TimestampEditor(Visible = Visible.Optional, Editable = Editable.Hidden)]
  168. [RequiredColumn]
  169. public virtual DateTime LastUpdate { get; set; }
  170. [CodeEditor(Visible = Visible.Optional, Editable = Editable.Hidden)]
  171. [RequiredColumn]
  172. public string LastUpdateBy { get; set; }
  173. [NullEditor]
  174. [RequiredColumn]
  175. public virtual DateTime Created { get; set; }
  176. [NullEditor]
  177. [RequiredColumn]
  178. public virtual string CreatedBy { get; set; }
  179. [NullEditor]
  180. [RequiredColumn]
  181. public Guid ID { get; set; }
  182. /// <summary>
  183. /// If the entity is deleted, holds the ID of the Deletion. Otherwise, it holds Guid.Empty
  184. /// </summary>
  185. [NullEditor]
  186. [DoNotSerialize]
  187. public Guid Deleted { get; set; }
  188. public static Type ClassVersion(Type t)
  189. {
  190. //Type t = MethodBase.GetCurrentMethod().DeclaringType;
  191. var ti = t.GetTypeInfo();
  192. var interfaces = ti.GetInterfaces();
  193. if (ti.GetInterfaces().Contains(typeof(IPersistent)))
  194. {
  195. if (ti.BaseType != null)
  196. throw new Exception(t.Name + " hase no Base Type");
  197. if (ti.BaseType.Equals(typeof(Entity)))
  198. throw new Exception(t.Name + " may not derive directly from TEntity");
  199. var props = t.GetTypeInfo().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
  200. if (props.Count() > 0)
  201. throw new Exception(t.Name + "may not declare properties");
  202. }
  203. return t.GetTypeInfo().BaseType;
  204. }
  205. //[NullEditor]
  206. //public List<EntityHistory> History { get; set; }
  207. protected override void Init()
  208. {
  209. base.Init();
  210. ID = Guid.Empty;
  211. Deleted = Guid.Empty;
  212. Created = DateTime.Now;
  213. CreatedBy = ClientFactory.UserID;
  214. LastUpdate = DateTime.Now;
  215. LastUpdateBy = ClientFactory.UserID;
  216. //History = new List<EntityHistory>();
  217. CheckSequence();
  218. }
  219. //public Entity() : base()
  220. //{
  221. // CommitChanges();
  222. //}
  223. //public Entity(Guid id) : base()
  224. //{
  225. // ID = id;
  226. // History = new List<EntityHistory>();
  227. // UserProperties = new Dictionary<string, Object>();
  228. // DataModel.InitializeEntity(this);
  229. // CheckSequence();
  230. // CommitChanges();
  231. //}
  232. public static bool IsEntityLinkValid<T, U>(Expression<Func<T, U>> expression, CoreRow arg) where U : IEntityLink
  233. {
  234. return arg.IsEntityLinkValid(expression);
  235. }
  236. /// <summary>
  237. /// 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)"/>)
  238. /// </summary>
  239. /// <typeparam name="T">The entity type</typeparam>
  240. /// <typeparam name="U">The entity link type</typeparam>
  241. /// <param name="expression">An expression to the entity link of type <typeparamref name="U"/></param>
  242. /// <param name="arg">The row representing the entity of type <typeparamref name="T"/></param>
  243. /// <returns>The ID on the entity link, or <c>null</c> if the entity link is invalid</returns>
  244. public static Guid? EntityLinkID<T, U>(Expression<Func<T, U>> expression, CoreRow arg) where U : IEntityLink
  245. {
  246. var col = CoreUtils.GetFullPropertyName(expression, ".");
  247. var id = arg.Get<Guid>(col + ".ID");
  248. if (id != Guid.Empty && arg.Get<Guid>(col + ".Deleted") == Guid.Empty)
  249. return id;
  250. return null;
  251. }
  252. private void CheckSequence()
  253. {
  254. if (this is ISequenceable)
  255. {
  256. var seq = (ISequenceable)this;
  257. if (seq.Sequence <= 0)
  258. seq.Sequence = CoreUtils.GenerateSequence();
  259. }
  260. }
  261. protected override void SetChanged(string name, object before, object after)
  262. {
  263. base.SetChanged(name, before, after);
  264. CheckTax(name, before, after);
  265. }
  266. private void CheckTax(string name, object before, object after)
  267. {
  268. if (this is ITaxable)
  269. {
  270. if (bTaxing)
  271. return;
  272. bTaxing = true;
  273. var taxable = this as ITaxable;
  274. if (name.Equals("ExTax"))
  275. {
  276. taxable.Tax = (double)after * (taxable.TaxRate / 100.0F);
  277. taxable.IncTax = (double)after + taxable.Tax;
  278. }
  279. else if (name.Equals("TaxRate"))
  280. {
  281. taxable.Tax = taxable.ExTax * ((double)after / 100.0F);
  282. taxable.IncTax = taxable.ExTax + taxable.Tax;
  283. }
  284. else if (name.Equals("Tax"))
  285. {
  286. taxable.IncTax = (double)after + taxable.Tax;
  287. }
  288. else if (name.Equals("IncTax"))
  289. {
  290. taxable.ExTax = (double)after / ((100.0F + taxable.TaxRate) / 100.0F);
  291. taxable.Tax = (double)after - taxable.ExTax;
  292. }
  293. bTaxing = false;
  294. }
  295. }
  296. protected override void DoPropertyChanged(string name, object before, object after)
  297. {
  298. if (!IsObserving())
  299. return;
  300. //CheckSequence();
  301. if (!name.Equals("LastUpdate"))
  302. LastUpdate = DateTime.Now;
  303. LastUpdateBy = ClientFactory.UserID;
  304. // This doesn;t work - keeps being updated to current date
  305. // Created => null ::Set ID = guid.empty -> now :: any other change -> unchanged!
  306. // Moved to Create(), should not simply be overwritten on deserialise from json
  307. //if (Created.Equals(DateTime.MinValue))
  308. //{
  309. // Created = DateTime.Now;
  310. // CreatedBy = ClientFactory.UserID;
  311. //}
  312. }
  313. #region Linked Properties
  314. // Why?
  315. [DoNotSerialize]
  316. private static readonly List<LinkedProperty> _LinkedProperties = new List<LinkedProperty>();
  317. public virtual void LinkProperty<TEntityLink, TLinkedEntity>(Expression<Func<TEntityLink, object>> source,
  318. Expression<Func<TLinkedEntity, object>> target)
  319. {
  320. var map = LinkedProperty.Create(source, target);
  321. if (!_LinkedProperties.Any(x =>
  322. x.Source.DeclaringType.Equals(typeof(TEntityLink)) && x.Source.Name.Equals(map.Source.Name) &&
  323. x.Source.Name.Equals(map.Source.Name) && x.Target.DeclaringType.Equals(typeof(TLinkedEntity)) &&
  324. x.Target.Name.Equals(map.Target.Name)))
  325. _LinkedProperties.Add(LinkedProperty.Create(source, target));
  326. }
  327. public IEnumerable<LinkedProperty> LinkedProperties(Type type)
  328. {
  329. var result = _LinkedProperties.Where(x =>
  330. (x.Source.DeclaringType.Equals(type) || x.Source.DeclaringType.IsSubclassOf(type)) &&
  331. (x.Target.DeclaringType.Equals(GetType()) || x.Target.DeclaringType.IsSubclassOf(GetType()))
  332. );
  333. return result;
  334. }
  335. #endregion
  336. }
  337. public interface ILicense<TLicenseToken> where TLicenseToken : LicenseToken
  338. {
  339. }
  340. public interface IPersistent
  341. {
  342. }
  343. public interface IRemotable
  344. {
  345. }
  346. //public interface IRemoteQuery
  347. //{
  348. //}
  349. //public interface IRemoteUpdate
  350. //{
  351. //}
  352. //public interface IRemoteDelete
  353. //{
  354. //}
  355. public interface ISequenceable
  356. {
  357. long Sequence { get; set; }
  358. }
  359. public interface IAutoIncrement<T, TType>
  360. {
  361. Expression<Func<T, TType>> AutoIncrementField();
  362. Filter<T> AutoIncrementFilter();
  363. }
  364. public interface INumericAutoIncrement<T> : IAutoIncrement<T, int>
  365. {
  366. }
  367. public interface IStringAutoIncrement<T> : IAutoIncrement<T, string>
  368. {
  369. string AutoIncrementFormat();
  370. }
  371. /// <summary>
  372. /// 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
  373. /// entity relationships.
  374. /// </summary>
  375. /// <remarks>
  376. /// This will cause a ManyToMany grid of <typeparamref name="TRight"/> to appear on all <typeparamref name="TLeft"/> editors.
  377. /// 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>
  378. /// vice versa, one must flag the entity with both <c>IManyToMany&lt;<typeparamref name="TLeft"/>, <typeparamref name="TRight"/>&gt;</c> and
  379. /// <c>IManyToMany&lt;<typeparamref name="TRight"/>, <typeparamref name="TLeft"/>&gt;</c>.
  380. /// </remarks>
  381. /// <typeparam name="TLeft"></typeparam>
  382. /// <typeparam name="TRight"></typeparam>
  383. public interface IManyToMany<TLeft, TRight> where TLeft : Entity where TRight : Entity
  384. {
  385. }
  386. public interface IOneToMany<TOne> where TOne : Entity
  387. {
  388. }
  389. public static class EntityFactory
  390. {
  391. public delegate object ObjectActivator(params object[] args);
  392. private static readonly Dictionary<Type, ObjectActivator> _cache = new Dictionary<Type, ObjectActivator>();
  393. public static ObjectActivator GetActivator<T>(ConstructorInfo ctor)
  394. {
  395. var type = ctor.DeclaringType;
  396. var paramsInfo = ctor.GetParameters();
  397. //create a single param of type object[]
  398. var param =
  399. Expression.Parameter(typeof(object[]), "args");
  400. var argsExp =
  401. new Expression[paramsInfo.Length];
  402. //pick each arg from the params array
  403. //and create a typed expression of them
  404. for (var i = 0; i < paramsInfo.Length; i++)
  405. {
  406. Expression index = Expression.Constant(i);
  407. var paramType = paramsInfo[i].ParameterType;
  408. Expression paramAccessorExp =
  409. Expression.ArrayIndex(param, index);
  410. Expression paramCastExp =
  411. Expression.Convert(paramAccessorExp, paramType);
  412. argsExp[i] = paramCastExp;
  413. }
  414. //make a NewExpression that calls the
  415. //ctor with the args we just created
  416. var newExp = Expression.New(ctor, argsExp);
  417. //create a lambda with the New
  418. //Expression as body and our param object[] as arg
  419. var lambda =
  420. Expression.Lambda(typeof(ObjectActivator), newExp, param);
  421. //compile it
  422. var compiled = (ObjectActivator)lambda.Compile();
  423. return compiled;
  424. }
  425. public static T CreateEntity<T>() where T : BaseObject
  426. {
  427. if (!_cache.ContainsKey(typeof(T)))
  428. {
  429. var ctor = typeof(T).GetConstructors().First();
  430. var activator = typeof(EntityFactory).GetMethod("GetActivator").MakeGenericMethod(typeof(T));
  431. _cache[typeof(T)] = GetActivator<T>(ctor);
  432. }
  433. var createdActivator = _cache[typeof(T)];
  434. return (T)createdActivator();
  435. }
  436. public static object CreateEntity(Type type)
  437. {
  438. if (!_cache.ContainsKey(type))
  439. {
  440. var ctor = type.GetConstructors().First();
  441. var activator = typeof(EntityFactory).GetMethod("GetActivator").MakeGenericMethod(type);
  442. _cache[type] = (ObjectActivator)activator.Invoke(null, new object[] { ctor });
  443. }
  444. var createdActivator = _cache[type];
  445. return createdActivator();
  446. }
  447. }
  448. }