Aggregate.cs 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. namespace InABox.Core
  6. {
  7. public static class AggregateUtils
  8. {
  9. private static string RemoveConvert(Expression expression)
  10. {
  11. // We were running a ToString on expression and removing the convert using string functions, however this failed when
  12. // .NET has a different string representation for .NET 6.0;
  13. // Compare "Login => Convert(Login.User.ID)" and "Login => Convert(Login.User.ID, Object)"
  14. if (expression is LambdaExpression lambda)
  15. {
  16. var body = lambda.Body;
  17. if (body is UnaryExpression unary && body.NodeType == ExpressionType.Convert)
  18. {
  19. var operand = unary.Operand;
  20. return operand.ToString();
  21. }
  22. return body.ToString();
  23. }
  24. // Probably not, but it is for now
  25. return expression.ToString();
  26. //String result = expression.ToString().Split(new String[] { "=>" }, StringSplitOptions.RemoveEmptyEntries).Last().Trim();
  27. //if (result.ToUpper().StartsWith("CONVERT("))
  28. // result = result.Split('(', ')')[1];
  29. //return result;
  30. }
  31. private static string ProcessConstantExpression(Expression expression)
  32. {
  33. var result = expression.ToString();
  34. return result;
  35. }
  36. public static string ProcessExpression(Expression expr)
  37. {
  38. if (expr.NodeType == ExpressionType.Convert)
  39. expr = ((UnaryExpression)expr).Operand;
  40. //if (expr.NodeType == ExpressionType.MemberAccess)
  41. //{
  42. // var result = Expression.Lambda(expr).Compile().DynamicInvoke();
  43. // return result == null ? "null" : result.ToString();
  44. //}
  45. if (expr is ConstantExpression) return ProcessConstantExpression(expr);
  46. if (expr is MemberExpression && ((MemberExpression)expr).Expression == null)
  47. {
  48. var result = Expression.Lambda(expr).Compile().DynamicInvoke();
  49. return expr.Type.IsDefault(result) ? "NULL" : expr.Type == typeof(string) ? string.Format("\"{0}\"", result) : result.ToString();
  50. }
  51. return string.Join(".", RemoveConvert(expr).Split('.').Skip(1));
  52. }
  53. }
  54. #region ComplexFormula
  55. public static class ComplexFormula
  56. {
  57. public static IComplexFormulaFieldNode Field(Type T, Type TResult, string field)
  58. {
  59. var type = typeof(ComplexFormulaFieldNode<,>).MakeGenericType(T, TResult);
  60. return (Activator.CreateInstance(type, field) as IComplexFormulaFieldNode)!;
  61. }
  62. }
  63. public interface IComplexFormulaNode
  64. {
  65. Type TType { get; }
  66. IComplexColumn ToColumn(string columnName);
  67. }
  68. public interface IComplexFormulaNode<TType, TResult> : IComplexFormulaNode
  69. {
  70. Type IComplexFormulaNode.TType => typeof(TType);
  71. IComplexColumn IComplexFormulaNode.ToColumn(string columnName) => new ComplexColumn<TType, TResult>(columnName, this);
  72. }
  73. #region FieldNode
  74. public interface IComplexFormulaFieldNode : IComplexFormulaNode
  75. {
  76. string GetField();
  77. }
  78. public class ComplexFormulaFieldNode<TType, TResult> : IComplexFormulaNode<TType, TResult>, IComplexFormulaFieldNode
  79. {
  80. //public Expression<Func<TType, TResult>> Expression { get; set; }
  81. public string Field { get; set; }
  82. public ComplexFormulaFieldNode(Expression<Func<TType, TResult>> expression)
  83. {
  84. Field = CoreUtils.GetFullPropertyName(expression, ".");
  85. }
  86. public ComplexFormulaFieldNode(string field)
  87. {
  88. Field = field;
  89. }
  90. string IComplexFormulaFieldNode.GetField() => Field;
  91. }
  92. #endregion
  93. #region ConstantNode
  94. public interface IComplexFormulaConstantNode : IComplexFormulaNode
  95. {
  96. object? GetConstant();
  97. }
  98. public class ComplexFormulaConstantNode<TType, TResult> : IComplexFormulaNode<TType, TResult>, IComplexFormulaConstantNode
  99. {
  100. public TResult Constant { get; set; }
  101. public ComplexFormulaConstantNode(TResult constant)
  102. {
  103. Constant = constant;
  104. }
  105. object? IComplexFormulaConstantNode.GetConstant() => Constant;
  106. }
  107. #endregion
  108. #region AggregateNode
  109. public interface IComplexFormulaAggregateNode : IComplexFormulaNode
  110. {
  111. Type TAggregate { get; }
  112. Type TResult { get; }
  113. IComplexFormulaNode GetExpression();
  114. AggregateCalculation GetCalculation();
  115. IFilter? GetFilter();
  116. Dictionary<string, string> GetLinks();
  117. }
  118. public class ComplexFormulaAggregateNode<TType, TAggregate, TResult> : IComplexFormulaNode<TType, TResult>, IComplexFormulaAggregateNode
  119. {
  120. public IComplexFormulaNode<TAggregate, TResult> Expression { get; set; }
  121. public AggregateCalculation Calculation { get; set; }
  122. public Filter<TAggregate>? Filter { get; set; }
  123. public Dictionary<Expression<Func<TAggregate, object?>>, Expression<Func<TType, object?>>> Links { get; }
  124. Type IComplexFormulaAggregateNode.TAggregate => typeof(TAggregate);
  125. Type IComplexFormulaAggregateNode.TResult => typeof(TResult);
  126. public ComplexFormulaAggregateNode(IComplexFormulaNode<TAggregate, TResult> expression, AggregateCalculation calculation, Filter<TAggregate>? filter, Dictionary<Expression<Func<TAggregate, object?>>, Expression<Func<TType, object?>>> links)
  127. {
  128. Expression = expression;
  129. Calculation = calculation;
  130. Filter = filter;
  131. Links = links;
  132. }
  133. public ComplexFormulaAggregateNode<TType, TAggregate, TResult> WithFilter(Filter<TAggregate> filter)
  134. {
  135. Filter = filter;
  136. return this;
  137. }
  138. public ComplexFormulaAggregateNode<TType, TAggregate, TResult> WithLink(Expression<Func<TAggregate, object?>> aggLink, Expression<Func<TType, object?>> masterLink)
  139. {
  140. Links.Add(aggLink, masterLink);
  141. return this;
  142. }
  143. public ComplexFormulaAggregateNode<TType, TAggregate, TResult> WithLinks(
  144. IEnumerable<KeyValuePair<Expression<Func<TAggregate, object?>>, Expression<Func<TType, object?>>>> links)
  145. {
  146. Links.AddRange(links);
  147. return this;
  148. }
  149. #region IComplexFormulaAggregateNode
  150. IComplexFormulaNode IComplexFormulaAggregateNode.GetExpression()
  151. {
  152. return Expression;
  153. }
  154. AggregateCalculation IComplexFormulaAggregateNode.GetCalculation()
  155. {
  156. return Calculation;
  157. }
  158. IFilter? IComplexFormulaAggregateNode.GetFilter()
  159. {
  160. return Filter;
  161. }
  162. Dictionary<string, string> IComplexFormulaAggregateNode.GetLinks()
  163. {
  164. return Links.ToDictionary(x => CoreUtils.GetFullPropertyName(x.Key, "."), x => CoreUtils.GetFullPropertyName(x.Value, "."));
  165. }
  166. #endregion
  167. }
  168. /// <summary>
  169. /// Represents an aggregate that has no links set; call <see cref="WithLink(Expression{Func{TAggregate, object?}}, Expression{Func{TType, object?}})"/> to
  170. /// set the link.
  171. /// </summary>
  172. /// <typeparam name="TType"></typeparam>
  173. /// <typeparam name="TAggregate"></typeparam>
  174. /// <typeparam name="TResult"></typeparam>
  175. public class ComplexFormulaPartialAggregateNode<TType, TAggregate, TResult>
  176. {
  177. public IComplexFormulaNode<TAggregate, TResult> Expression { get; set; }
  178. public AggregateCalculation Calculation { get; set; }
  179. public Filter<TAggregate>? Filter { get; set; }
  180. public ComplexFormulaPartialAggregateNode(IComplexFormulaNode<TAggregate, TResult> expression, AggregateCalculation calculation, Filter<TAggregate>? filter)
  181. {
  182. Expression = expression;
  183. Calculation = calculation;
  184. Filter = filter;
  185. }
  186. public ComplexFormulaPartialAggregateNode<TType, TAggregate, TResult> WithFilter(Filter<TAggregate> filter)
  187. {
  188. Filter = filter;
  189. return this;
  190. }
  191. public ComplexFormulaAggregateNode<TType, TAggregate, TResult> WithLink(Expression<Func<TAggregate, object?>> aggLink, Expression<Func<TType, object?>> masterLink)
  192. {
  193. var node = new ComplexFormulaAggregateNode<TType, TAggregate, TResult>(Expression, Calculation, Filter, new Dictionary<Expression<Func<TAggregate, object?>>, Expression<Func<TType, object?>>>());
  194. node.Links.Add(aggLink, masterLink);
  195. return node;
  196. }
  197. public ComplexFormulaAggregateNode<TType, TAggregate, TResult> WithLinks(
  198. IEnumerable<KeyValuePair<Expression<Func<TAggregate, object?>>, Expression<Func<TType, object?>>>> links)
  199. {
  200. return new ComplexFormulaAggregateNode<TType, TAggregate, TResult>(Expression, Calculation, Filter,
  201. new Dictionary<Expression<Func<TAggregate, object?>>, Expression<Func<TType, object?>>>().AddRange(links));
  202. }
  203. public ComplexFormulaAggregateNode<TType, TAggregate, TResult> WithLinks(
  204. Dictionary<Expression<Func<TAggregate, object?>>, Expression<Func<TType, object?>>> links)
  205. {
  206. return new ComplexFormulaAggregateNode<TType, TAggregate, TResult>(Expression, Calculation, Filter, links);
  207. }
  208. public ComplexFormulaAggregateNode<TType, TAggregate, TResult> WithLinks(
  209. params KeyValuePair<Expression<Func<TAggregate, object?>>, Expression<Func<TType, object?>>>[] links)
  210. {
  211. var node = new ComplexFormulaAggregateNode<TType, TAggregate, TResult>(Expression, Calculation, Filter, new Dictionary<Expression<Func<TAggregate, object?>>, Expression<Func<TType, object?>>>());
  212. node.Links.AddRange(links);
  213. return node;
  214. }
  215. }
  216. #endregion
  217. #region FormulaNode
  218. public interface IComplexFormulaFormulaNode : IComplexFormulaNode
  219. {
  220. Type TResult { get; }
  221. IEnumerable<IComplexFormulaNode> GetOperands();
  222. FormulaOperator GetOperator();
  223. }
  224. public class ComplexFormulaFormulaNode<TType, TResult> : IComplexFormulaNode<TType, TResult>, IComplexFormulaFormulaNode
  225. {
  226. public IComplexFormulaNode<TType, TResult>[] Operands { get; set; }
  227. public FormulaOperator Operator { get; set; }
  228. Type IComplexFormulaFormulaNode.TResult => typeof(TResult);
  229. public ComplexFormulaFormulaNode(IComplexFormulaNode<TType, TResult>[] operands, FormulaOperator op)
  230. {
  231. Operands = operands;
  232. Operator = op;
  233. }
  234. IEnumerable<IComplexFormulaNode> IComplexFormulaFormulaNode.GetOperands() => Operands;
  235. FormulaOperator IComplexFormulaFormulaNode.GetOperator() => Operator;
  236. }
  237. #endregion
  238. #region ConditionNode
  239. public class ComplexFormulaPartial0ConditionNode<TType, TCondition, TValue>
  240. {
  241. public IComplexFormulaNode<TType, TCondition> Left { get; set; }
  242. public IComplexFormulaNode<TType, TCondition> Right { get; set; }
  243. public Condition Condition { get; set; }
  244. public ComplexFormulaPartial0ConditionNode(IComplexFormulaNode<TType, TCondition> left, IComplexFormulaNode<TType, TCondition> right, Condition condition)
  245. {
  246. Left = left;
  247. Right = right;
  248. Condition = condition;
  249. }
  250. public ComplexFormulaPartial1ConditionNode<TType, TCondition, TValue> Then(IComplexFormulaNode<TType, TValue> then)
  251. {
  252. return new ComplexFormulaPartial1ConditionNode<TType, TCondition, TValue>(Left, Right, Condition, then);
  253. }
  254. }
  255. public class ComplexFormulaPartial1ConditionNode<TType, TCondition, TValue>
  256. {
  257. public IComplexFormulaNode<TType, TCondition> Left { get; set; }
  258. public IComplexFormulaNode<TType, TCondition> Right { get; set; }
  259. public IComplexFormulaNode<TType, TValue> True { get; set; }
  260. public Condition Condition { get; set; }
  261. public ComplexFormulaPartial1ConditionNode(IComplexFormulaNode<TType, TCondition> left, IComplexFormulaNode<TType, TCondition> right, Condition condition, IComplexFormulaNode<TType, TValue> trueValue)
  262. {
  263. Left = left;
  264. Right = right;
  265. Condition = condition;
  266. True = trueValue;
  267. }
  268. public ComplexFormulaConditionNode<TType, TCondition, TValue> Else(IComplexFormulaNode<TType, TValue> elseValue)
  269. {
  270. return new ComplexFormulaConditionNode<TType, TCondition, TValue>(Left, Right, True, elseValue, Condition);
  271. }
  272. }
  273. public interface IComplexFormulaConditionNode : IComplexFormulaNode
  274. {
  275. public Type TCondition { get; }
  276. public IComplexFormulaNode Left { get; }
  277. public IComplexFormulaNode Right { get; }
  278. public IComplexFormulaNode True { get; }
  279. public IComplexFormulaNode False { get; }
  280. public Condition Condition { get; }
  281. }
  282. public class ComplexFormulaConditionNode<TType, TCondition, TValue> : IComplexFormulaNode<TType, TValue>, IComplexFormulaConditionNode
  283. {
  284. Type IComplexFormulaConditionNode.TCondition => typeof(TCondition);
  285. public IComplexFormulaNode<TType, TCondition> Left { get; set; }
  286. public IComplexFormulaNode<TType, TCondition> Right { get; set; }
  287. public IComplexFormulaNode<TType, TValue> True { get; set; }
  288. public IComplexFormulaNode<TType, TValue> False { get; set; }
  289. public Condition Condition { get; set; }
  290. public ComplexFormulaConditionNode(IComplexFormulaNode<TType, TCondition> left, IComplexFormulaNode<TType, TCondition> right, IComplexFormulaNode<TType, TValue> trueValue, IComplexFormulaNode<TType, TValue> falseValue, Condition condition)
  291. {
  292. Left = left;
  293. Right = right;
  294. True = trueValue;
  295. False = falseValue;
  296. Condition = condition;
  297. }
  298. IComplexFormulaNode IComplexFormulaConditionNode.Left => Left;
  299. IComplexFormulaNode IComplexFormulaConditionNode.Right => Right;
  300. IComplexFormulaNode IComplexFormulaConditionNode.True => True;
  301. IComplexFormulaNode IComplexFormulaConditionNode.False => False;
  302. }
  303. #endregion
  304. #region Generator + Interface
  305. public interface IComplexFormulaGenerator
  306. {
  307. IComplexFormulaNode GetFormula();
  308. }
  309. public abstract class ComplexFormulaGenerator
  310. {
  311. public static IComplexFormulaNode<TType, TResult> Property<TType, TResult>(Expression<Func<TType, TResult>> expression)
  312. {
  313. return new ComplexFormulaFieldNode<TType, TResult>(expression);
  314. }
  315. public static IComplexFormulaNode<TType, TResult> Formula<TType, TResult>(FormulaOperator op, params IComplexFormulaNode<TType, TResult>[] operands)
  316. {
  317. return new ComplexFormulaFormulaNode<TType, TResult>(operands, op);
  318. }
  319. public static IComplexFormulaNode<TType, TResult> Aggregate<TType, TAggregate, TResult>(
  320. AggregateCalculation calculation,
  321. IComplexFormulaNode<TAggregate, TResult> expression,
  322. KeyValuePair<Expression<Func<TAggregate, object?>>, Expression<Func<TType, object?>>>[] links,
  323. Filter<TAggregate>? filter = null
  324. )
  325. {
  326. return new ComplexFormulaAggregateNode<TType, TAggregate, TResult>(expression, calculation, filter, links.ToDictionary(x => x.Key, x => x.Value));
  327. }
  328. public static ComplexFormulaPartialAggregateNode<TType, TAggregate, TResult> Aggregate<TType, TAggregate, TResult>(
  329. AggregateCalculation calculation,
  330. IComplexFormulaNode<TAggregate, TResult> expression,
  331. Filter<TAggregate>? filter = null
  332. )
  333. {
  334. return new ComplexFormulaPartialAggregateNode<TType, TAggregate, TResult>(expression, calculation, filter);
  335. }
  336. public static IComplexFormulaNode<TType, TResult> Constant<TType, TResult>(TResult constant)
  337. {
  338. return new ComplexFormulaConstantNode<TType, TResult>(constant);
  339. }
  340. public static ComplexFormulaPartial0ConditionNode<TType, TCondition, TValue> If<TType, TCondition, TValue>(
  341. IComplexFormulaNode<TType, TCondition> left,
  342. Condition condition,
  343. IComplexFormulaNode<TType, TCondition> right)
  344. {
  345. return new ComplexFormulaPartial0ConditionNode<TType, TCondition, TValue>(left, right, condition);
  346. }
  347. }
  348. public interface IComplexFormulaGenerator<TType, TResult>
  349. {
  350. IComplexFormulaNode<TType, TResult> Property(Expression<Func<TType, TResult>> expression);
  351. IComplexFormulaNode<TType, TResult> Formula(FormulaOperator op, params IComplexFormulaNode<TType, TResult>[] operands);
  352. IComplexFormulaNode<TType, TResult> Aggregate<TAggregate>(
  353. AggregateCalculation calculation,
  354. Func<IComplexFormulaGenerator<TAggregate, TResult>, IComplexFormulaNode<TAggregate, TResult>> expression,
  355. KeyValuePair<Expression<Func<TAggregate, object?>>, Expression<Func<TType, object?>>>[] links,
  356. Filter<TAggregate>? filter = null);
  357. IComplexFormulaNode<TType, TResult> Constant(TResult constant);
  358. ComplexFormulaPartialAggregateNode<TType, TAggregate, TResult> Aggregate<TAggregate>(
  359. AggregateCalculation calculation,
  360. Func<IComplexFormulaGenerator<TAggregate, TResult>, IComplexFormulaNode<TAggregate, TResult>> expression,
  361. Filter<TAggregate>? filter = null
  362. );
  363. ComplexFormulaPartial0ConditionNode<TType, TCondition, TResult> If<TCondition>(
  364. Func<IComplexFormulaGenerator<TType, TCondition>, IComplexFormulaNode<TType, TCondition>> left,
  365. Condition condition,
  366. Func<IComplexFormulaGenerator<TType, TCondition>, IComplexFormulaNode<TType, TCondition>> right);
  367. }
  368. internal class InternalComplexFormulaGenerator<TType, TResult> : IComplexFormulaGenerator<TType, TResult>
  369. {
  370. public IComplexFormulaNode<TType, TResult> Property(Expression<Func<TType, TResult>> expression)
  371. {
  372. return ComplexFormulaGenerator.Property(expression);
  373. }
  374. public IComplexFormulaNode<TType, TResult> Formula(FormulaOperator op, params IComplexFormulaNode<TType, TResult>[] operands)
  375. {
  376. return ComplexFormulaGenerator.Formula(op, operands);
  377. }
  378. public IComplexFormulaNode<TType, TResult> Aggregate<TAggregate>(
  379. AggregateCalculation calculation,
  380. Func<IComplexFormulaGenerator<TAggregate, TResult>, IComplexFormulaNode<TAggregate, TResult>> expression,
  381. KeyValuePair<Expression<Func<TAggregate, object?>>, Expression<Func<TType, object?>>>[] links,
  382. Filter<TAggregate>? filter = null)
  383. {
  384. return ComplexFormulaGenerator.Aggregate(calculation, expression(new InternalComplexFormulaGenerator<TAggregate, TResult>()), links, filter);
  385. }
  386. public IComplexFormulaNode<TType, TResult> Constant(TResult constant)
  387. {
  388. return ComplexFormulaGenerator.Constant<TType, TResult>(constant);
  389. }
  390. public ComplexFormulaPartialAggregateNode<TType, TAggregate, TResult> Aggregate<TAggregate>(
  391. AggregateCalculation calculation,
  392. Func<IComplexFormulaGenerator<TAggregate, TResult>, IComplexFormulaNode<TAggregate, TResult>> expression,
  393. Filter<TAggregate>? filter = null)
  394. {
  395. return ComplexFormulaGenerator.Aggregate<TType, TAggregate, TResult>(calculation, expression(new InternalComplexFormulaGenerator<TAggregate, TResult>()), filter);
  396. }
  397. public ComplexFormulaPartial0ConditionNode<TType, TCondition, TResult> If<TCondition>(
  398. Func<IComplexFormulaGenerator<TType, TCondition>, IComplexFormulaNode<TType, TCondition>> left,
  399. Condition condition,
  400. Func<IComplexFormulaGenerator<TType, TCondition>, IComplexFormulaNode<TType, TCondition>> right)
  401. {
  402. var generator = new InternalComplexFormulaGenerator<TType, TCondition>();
  403. return ComplexFormulaGenerator.If<TType, TCondition, TResult>(
  404. left(generator),
  405. condition,
  406. right(generator));
  407. }
  408. }
  409. public abstract class ComplexFormulaGenerator<TType, TResult> : ComplexFormulaGenerator, IComplexFormulaGenerator<TType, TResult>, IComplexFormulaGenerator
  410. {
  411. public abstract IComplexFormulaNode<TType, TResult> GetFormula();
  412. #region Internals
  413. IComplexFormulaNode IComplexFormulaGenerator.GetFormula() => GetFormula();
  414. private readonly InternalComplexFormulaGenerator<TType, TResult> InternalGenerator = new InternalComplexFormulaGenerator<TType, TResult>();
  415. public IComplexFormulaNode<TType, TResult> Aggregate<TAggregate>(
  416. AggregateCalculation calculation,
  417. Func<IComplexFormulaGenerator<TAggregate, TResult>, IComplexFormulaNode<TAggregate, TResult>> expression,
  418. KeyValuePair<Expression<Func<TAggregate, object?>>, Expression<Func<TType, object?>>>[] links,
  419. Filter<TAggregate>? filter = null)
  420. {
  421. return ((IComplexFormulaGenerator<TType, TResult>)InternalGenerator).Aggregate(calculation, expression, links, filter);
  422. }
  423. public IComplexFormulaNode<TType, TResult> Formula(FormulaOperator op, params IComplexFormulaNode<TType, TResult>[] operands)
  424. {
  425. return ((IComplexFormulaGenerator<TType, TResult>)InternalGenerator).Formula(op, operands);
  426. }
  427. public IComplexFormulaNode<TType, TResult> Property(Expression<Func<TType, TResult>> epxression)
  428. {
  429. return ((IComplexFormulaGenerator<TType, TResult>)InternalGenerator).Property(epxression);
  430. }
  431. public IComplexFormulaNode<TType, TResult> Constant(TResult constant)
  432. {
  433. return ((IComplexFormulaGenerator<TType, TResult>)InternalGenerator).Constant(constant);
  434. }
  435. public ComplexFormulaPartialAggregateNode<TType, TAggregate, TResult> Aggregate<TAggregate>(
  436. AggregateCalculation calculation,
  437. Func<IComplexFormulaGenerator<TAggregate, TResult>, IComplexFormulaNode<TAggregate, TResult>> expression,
  438. Filter<TAggregate>? filter = null)
  439. {
  440. return ((IComplexFormulaGenerator<TType, TResult>)InternalGenerator).Aggregate(calculation, expression, filter);
  441. }
  442. public ComplexFormulaPartial0ConditionNode<TType, TCondition, TResult> If<TCondition>(Func<IComplexFormulaGenerator<TType, TCondition>, IComplexFormulaNode<TType, TCondition>> left, Condition condition, Func<IComplexFormulaGenerator<TType, TCondition>, IComplexFormulaNode<TType, TCondition>> right)
  443. {
  444. return ((IComplexFormulaGenerator<TType, TResult>)InternalGenerator).If(left, condition, right);
  445. }
  446. #endregion
  447. }
  448. public class ComplexFormulaAttribute : Attribute
  449. {
  450. public IComplexFormulaGenerator Generator { get; }
  451. public ComplexFormulaAttribute(Type generator)
  452. {
  453. var obj = Activator.CreateInstance(generator);
  454. if(obj is IComplexFormulaGenerator g)
  455. {
  456. Generator = g;
  457. }
  458. else
  459. {
  460. throw new Exception($"{nameof(ComplexFormulaAttribute)}: {generator} is not a {typeof(IComplexFormulaGenerator)}!");
  461. }
  462. }
  463. }
  464. #endregion
  465. #endregion
  466. #region Aggregates
  467. public enum AggregateCalculation
  468. {
  469. None,
  470. Sum,
  471. Count,
  472. Maximum,
  473. Minimum,
  474. Average,
  475. Concat
  476. }
  477. public interface ICoreAggregate<TType, TProp>
  478. {
  479. Expression<Func<TType, TProp>> Aggregate { get; }
  480. AggregateCalculation Calculation { get; }
  481. }
  482. public abstract class CoreAggregate<TType, TProp> : ICoreAggregate<TType, TProp>
  483. {
  484. public abstract Expression<Func<TType, TProp>> Aggregate { get; }
  485. public abstract AggregateCalculation Calculation { get; }
  486. public string GetAggregate()
  487. {
  488. return string.Join(".", Aggregate.ToString().Split('.').Skip(1));
  489. }
  490. }
  491. public interface ICoreAggregate<TMaster, TDetail, TProp>
  492. {
  493. Expression<Func<TDetail, TProp>> Aggregate { get; }
  494. Filter<TDetail>? Filter { get; }
  495. Dictionary<Expression<Func<TDetail, object?>>, Expression<Func<TMaster, object?>>> Links { get; }
  496. AggregateCalculation Calculation { get; }
  497. Dictionary<string, string> GetLinks();
  498. }
  499. public abstract class CoreAggregate<TMaster, TDetail, TProp> : ICoreAggregate<TMaster, TDetail, TProp>
  500. {
  501. public abstract Expression<Func<TDetail, TProp>> Aggregate { get; }
  502. public virtual Filter<TDetail>? Filter => null;
  503. public abstract Dictionary<Expression<Func<TDetail, object?>>, Expression<Func<TMaster, object?>>> Links { get; }
  504. public Dictionary<string, string> GetLinks()
  505. {
  506. var result = new Dictionary<string, string>();
  507. foreach (var link in Links)
  508. {
  509. var childkey = AggregateUtils.ProcessExpression(link.Key); // String.Join(".", link.Key.ToString().Split('.').Skip(1));
  510. var parentkey = AggregateUtils.ProcessExpression(link.Value); // String.Join(".", link.Value.ToString().Split('.').Skip(1);
  511. result[childkey] = parentkey;
  512. }
  513. return result;
  514. }
  515. public abstract AggregateCalculation Calculation { get; }
  516. public string GetAggregate()
  517. {
  518. return string.Join(".", Aggregate.ToString().Split('.').Skip(1));
  519. }
  520. }
  521. [Obsolete]
  522. public class AggregateAttribute : Attribute
  523. {
  524. public AggregateAttribute(Type calculator)
  525. {
  526. Calculator = Activator.CreateInstance(calculator);
  527. }
  528. public object Calculator { get; }
  529. public Type Source => GetSource();
  530. public AggregateCalculation Calculation => GetCalculation();
  531. public string Aggregate => GetAggregate();
  532. public Dictionary<string, string> Links => GetLinks();
  533. public IFilter? Filter => GetFilter();
  534. #region Internal (Reflection) functions
  535. private Type GetSource()
  536. {
  537. var intf = Calculator.GetType().GetInterfaces()
  538. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,,>));
  539. if (intf != null)
  540. return intf.GenericTypeArguments[1];
  541. intf = Calculator.GetType().GetInterfaces()
  542. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,>));
  543. if (intf != null)
  544. return intf.GenericTypeArguments[0];
  545. throw new Exception("Unable to Locate Type Information for Aggregate");
  546. }
  547. private string GetAggregate()
  548. {
  549. var intf = Calculator.GetType().GetInterfaces()
  550. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,,>));
  551. if (intf == null)
  552. {
  553. intf = Calculator.GetType().GetInterfaces()
  554. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,>));
  555. }
  556. if (intf != null)
  557. {
  558. var prop = intf.GetProperty("Aggregate");
  559. if (prop != null)
  560. {
  561. var obj = prop.GetValue(Calculator);
  562. if (obj != null)
  563. {
  564. var expr = obj as Expression;
  565. if (expr != null) return AggregateUtils.ProcessExpression(expr);
  566. }
  567. }
  568. }
  569. return "";
  570. }
  571. private Dictionary<string, string> GetLinks()
  572. {
  573. var intf = Calculator.GetType().GetInterfaces()
  574. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,,>));
  575. if (intf != null)
  576. {
  577. var method = intf.GetMethod("GetLinks");
  578. if (method != null)
  579. {
  580. var dict = method.Invoke(Calculator, new object[] { });
  581. return (dict as Dictionary<string, string>)!;
  582. }
  583. }
  584. return new Dictionary<string, string>();
  585. }
  586. private AggregateCalculation GetCalculation()
  587. {
  588. var intf = Calculator.GetType().GetInterfaces()
  589. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,,>));
  590. if (intf == null)
  591. intf = Calculator.GetType().GetInterfaces()
  592. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,>));
  593. if (intf != null)
  594. {
  595. var prop = intf.GetProperty("Calculation");
  596. if (prop != null)
  597. return (AggregateCalculation)prop.GetValue(Calculator);
  598. }
  599. return AggregateCalculation.None;
  600. }
  601. private IFilter? GetFilter()
  602. {
  603. var intf = Calculator.GetType().GetInterfaces()
  604. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,,>));
  605. if (intf != null)
  606. {
  607. var prop = intf.GetProperty("Filter");
  608. if (prop != null)
  609. return prop.GetValue(Calculator) as IFilter;
  610. }
  611. return null;
  612. }
  613. #endregion
  614. }
  615. #endregion
  616. #region Formulas
  617. public enum FormulaType
  618. {
  619. Virtual,
  620. Permanent
  621. }
  622. public enum FormulaOperator
  623. {
  624. None,
  625. /// <summary>
  626. /// Add the values together. If no values are provided, the result is 0
  627. /// </summary>
  628. Add,
  629. /// <summary>
  630. /// Subtract all values from the first value. If no values are
  631. /// provided, the result is 0; if only one value is provided, the
  632. /// result is the negation of the input.
  633. /// </summary>
  634. Subtract,
  635. /// <summary>
  636. /// Multiply the values together. If no values are provided, the result is 1
  637. /// </summary>
  638. Multiply,
  639. /// <summary>
  640. /// Divide all values from the first value. If no values are
  641. /// provided, the result is 1; if only one value is provided, the
  642. /// result is the reciprocal of the input.
  643. /// </summary>
  644. Divide,
  645. /// <summary>
  646. /// Take the minimum of all values.
  647. /// </summary>
  648. Minumum,
  649. /// <summary>
  650. /// Take the maximum of all values.
  651. /// </summary>
  652. Maximum,
  653. [Obsolete]
  654. Constant,
  655. /// <summary>
  656. /// Formats all the operands using the first string
  657. /// </summary>
  658. Format
  659. }
  660. public interface IFormula<TType, TProp>
  661. {
  662. Expression<Func<TType, TProp>> Value { get; }
  663. Expression<Func<TType, TProp>>[] Modifiers { get; }
  664. FormulaOperator Operator { get; }
  665. FormulaType Type { get; }
  666. }
  667. public interface IFormula
  668. {
  669. String Value { get; }
  670. String[] Modifiers { get; }
  671. FormulaOperator Operator { get; }
  672. FormulaType Type { get; }
  673. }
  674. [Obsolete]
  675. public class FormulaAttribute : Attribute, IFormula
  676. {
  677. public FormulaAttribute(Type calculator)
  678. {
  679. Calculator = Activator.CreateInstance(calculator);
  680. }
  681. public object Calculator { get; }
  682. public string Value => GetExpressionName("Value");
  683. public string[] Modifiers => GetExpressionNames("Modifiers");
  684. public FormulaOperator Operator => GetFormulaOperator();
  685. public FormulaType Type => GetFormulaType();
  686. #region Internal (Reflection) functions
  687. private FormulaOperator GetFormulaOperator()
  688. {
  689. var intf = Calculator.GetType().GetInterfaces()
  690. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IFormula<,>));
  691. if (intf != null)
  692. {
  693. var prop = intf.GetProperty("Operator");
  694. if (prop != null)
  695. return (FormulaOperator)prop.GetValue(Calculator);
  696. }
  697. return FormulaOperator.None;
  698. }
  699. private FormulaType GetFormulaType()
  700. {
  701. var intf = Calculator.GetType().GetInterfaces()
  702. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IFormula<,>));
  703. if (intf != null)
  704. {
  705. var prop = intf.GetProperty("Type");
  706. if (prop != null)
  707. return (FormulaType)prop.GetValue(Calculator);
  708. }
  709. return FormulaType.Virtual;
  710. }
  711. private string GetExpressionName(string property)
  712. {
  713. var intf = Calculator.GetType().GetInterfaces()
  714. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IFormula<,>));
  715. if (intf != null)
  716. {
  717. var prop = intf.GetProperty(property);
  718. if (prop != null)
  719. {
  720. if(prop.GetValue(Calculator) is LambdaExpression expr)
  721. {
  722. var result = AggregateUtils.ProcessExpression(expr.Body);
  723. return result;
  724. }
  725. }
  726. }
  727. return "";
  728. }
  729. private string[] GetExpressionNames(string property)
  730. {
  731. var intf = Calculator.GetType().GetInterfaces()
  732. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IFormula<,>));
  733. if (intf != null)
  734. {
  735. var prop = intf.GetProperty(property);
  736. if (prop != null)
  737. {
  738. if(prop.GetValue(Calculator) is LambdaExpression[] expressions)
  739. {
  740. var result = new List<string>();
  741. foreach (var expression in expressions)
  742. result.Add(AggregateUtils.ProcessExpression(expression.Body));
  743. return result.ToArray();
  744. }
  745. //return expressions.Select(x => x.Body is ConstantExpression ? ProcessConstantExpression(x.Body) : String.Join(".", RemoveConvert(x.Body).Split('.').Skip(1))).ToArray();
  746. }
  747. }
  748. return new string[] { };
  749. }
  750. #endregion
  751. }
  752. #endregion
  753. #region Conditions
  754. public enum Condition
  755. {
  756. None,
  757. Equals,
  758. NotEqual,
  759. GreaterThan,
  760. GreaterThanOrEqualTo,
  761. LessThan,
  762. LessThanOrEqualTo
  763. }
  764. public enum ConditionType
  765. {
  766. Virtual,
  767. Permanent
  768. }
  769. public interface ICondition<TType, TProp, TValue>
  770. {
  771. Expression<Func<TType, TProp>> Left { get; }
  772. Condition Condition { get; }
  773. Expression<Func<TType, TProp>> Right { get; }
  774. Expression<Func<TType, TValue>> True { get; }
  775. Expression<Func<TType, TValue>> False { get; }
  776. ConditionType Type { get; }
  777. }
  778. [Obsolete]
  779. public class ConditionAttribute : Attribute
  780. {
  781. public ConditionAttribute(Type calculator)
  782. {
  783. Calculator = Activator.CreateInstance(calculator);
  784. }
  785. public object Calculator { get; }
  786. public string Left => GetExpressionName("Left");
  787. public Condition Condition => GetCondition();
  788. public string Right => GetExpressionName("Right");
  789. public string True => GetExpressionName("True");
  790. public string False => GetExpressionName("False");
  791. public ConditionType Type => GetConditionType();
  792. #region Internal (Reflection) functions
  793. private Condition GetCondition()
  794. {
  795. var intf = Calculator.GetType().GetInterfaces()
  796. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICondition<,,>));
  797. if (intf != null)
  798. {
  799. var prop = intf.GetProperty("Condition");
  800. if (prop != null)
  801. return (Condition)prop.GetValue(Calculator);
  802. }
  803. return Condition.None;
  804. }
  805. private ConditionType GetConditionType()
  806. {
  807. var intf = Calculator.GetType().GetInterfaces()
  808. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICondition<,,>));
  809. if (intf != null)
  810. {
  811. var prop = intf.GetProperty("Type");
  812. if (prop != null)
  813. return (ConditionType)prop.GetValue(Calculator);
  814. }
  815. return ConditionType.Virtual;
  816. }
  817. private string GetExpressionName(string property)
  818. {
  819. var intf = Calculator.GetType().GetInterfaces()
  820. .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICondition<,,>));
  821. if (intf != null)
  822. {
  823. var prop = intf.GetProperty(property);
  824. if (prop?.GetValue(Calculator) is LambdaExpression expr)
  825. {
  826. return AggregateUtils.ProcessExpression(expr.Body);
  827. }
  828. }
  829. return "";
  830. }
  831. #endregion
  832. }
  833. #endregion
  834. #region ChildEntity
  835. public interface IChildEntityDefinition
  836. {
  837. string ParentColumn { get; }
  838. Type EntityType { get; }
  839. IFilter? Filter { get; }
  840. ISortOrder? Sort { get; }
  841. }
  842. public interface IChildEntityDefinition<TEntity> : IChildEntityDefinition
  843. where TEntity : Entity
  844. {
  845. new Filter<TEntity>? Filter { get; }
  846. new SortOrder<TEntity>? Sort { get; }
  847. Expression<Func<TEntity, Guid>> Parent { get; }
  848. Type IChildEntityDefinition.EntityType => typeof(TEntity);
  849. IFilter? IChildEntityDefinition.Filter => Filter;
  850. ISortOrder? IChildEntityDefinition.Sort => Sort;
  851. string IChildEntityDefinition.ParentColumn => CoreUtils.GetFullPropertyName(Parent, ".");
  852. }
  853. public class ChildEntityAttribute : Attribute
  854. {
  855. public IChildEntityDefinition Calculator { get; set; }
  856. public ChildEntityAttribute(Type definition)
  857. {
  858. Calculator = (Activator.CreateInstance(definition) as IChildEntityDefinition)
  859. ?? throw new Exception($"{definition} is not an {nameof(IChildEntityDefinition)}");
  860. }
  861. }
  862. #endregion
  863. }