Aggregate.cs 38 KB

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