Aggregate.cs 38 KB

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