Aggregate.cs 43 KB

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