Aggregate.cs 43 KB

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