Aggregate.cs 43 KB

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