using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; namespace InABox.Core { public static class AggregateUtils { private static string RemoveConvert(Expression expression) { // We were running a ToString on expression and removing the convert using string functions, however this failed when // .NET has a different string representation for .NET 6.0; // Compare "Login => Convert(Login.User.ID)" and "Login => Convert(Login.User.ID, Object)" if (expression is LambdaExpression lambda) { var body = lambda.Body; if (body is UnaryExpression unary && body.NodeType == ExpressionType.Convert) { var operand = unary.Operand; return operand.ToString(); } return body.ToString(); } // Probably not, but it is for now return expression.ToString(); //String result = expression.ToString().Split(new String[] { "=>" }, StringSplitOptions.RemoveEmptyEntries).Last().Trim(); //if (result.ToUpper().StartsWith("CONVERT(")) // result = result.Split('(', ')')[1]; //return result; } private static string ProcessConstantExpression(Expression expression) { var result = expression.ToString(); return result; } public static string ProcessExpression(Expression expr) { if (expr.NodeType == ExpressionType.Convert) expr = ((UnaryExpression)expr).Operand; //if (expr.NodeType == ExpressionType.MemberAccess) //{ // var result = Expression.Lambda(expr).Compile().DynamicInvoke(); // return result == null ? "null" : result.ToString(); //} if (expr is ConstantExpression) return ProcessConstantExpression(expr); if (expr is MemberExpression && ((MemberExpression)expr).Expression == null) { var result = Expression.Lambda(expr).Compile().DynamicInvoke(); return expr.Type.IsDefault(result) ? "NULL" : expr.Type == typeof(string) ? string.Format("\"{0}\"", result) : result.ToString(); } return string.Join(".", RemoveConvert(expr).Split('.').Skip(1)); } } #region ComplexFormula public static class ComplexFormula { public static IComplexFormulaFieldNode Field(Type T, Type TResult, string field) { var type = typeof(ComplexFormulaFieldNode<,>).MakeGenericType(T, TResult); return (Activator.CreateInstance(type, field) as IComplexFormulaFieldNode)!; } } public interface IComplexFormulaNode { Type TType { get; } IComplexColumn ToColumn(string columnName); } public interface IComplexFormulaNode : IComplexFormulaNode { Type IComplexFormulaNode.TType => typeof(TType); IComplexColumn IComplexFormulaNode.ToColumn(string columnName) => new ComplexColumn(columnName, this); } #region FieldNode public interface IComplexFormulaFieldNode : IComplexFormulaNode { string GetField(); } public class ComplexFormulaFieldNode : IComplexFormulaNode, IComplexFormulaFieldNode { public string Field { get; set; } public ComplexFormulaFieldNode(Expression> expression) { Field = CoreUtils.GetFullPropertyName(expression, "."); } public ComplexFormulaFieldNode(string field) { Field = field; } string IComplexFormulaFieldNode.GetField() => Field; } #endregion #region ConstantNode public interface IComplexFormulaConstantNode : IComplexFormulaNode { object? GetConstant(); } public class ComplexFormulaConstantNode : IComplexFormulaNode, IComplexFormulaConstantNode { public TResult Constant { get; set; } public ComplexFormulaConstantNode(TResult constant) { Constant = constant; } object? IComplexFormulaConstantNode.GetConstant() => Constant; } #endregion #region AggregateNode public interface IComplexFormulaAggregateNode : IComplexFormulaNode { Type TAggregate { get; } Type TResult { get; } IComplexFormulaNode GetExpression(); AggregateCalculation GetCalculation(); IFilter? GetFilter(); Dictionary GetLinks(); } /// /// Represents an aggregate, to form a single value from another table in the database. /// /// The type of the parent table, on which this formula is defined. /// The type of the child table, which is being aggregated. /// The type of the property which is being aggregated. /// /// The type of the result of the aggregate. In most cases, this will be the same as , /// except for aggregates like , which will always be . /// public class ComplexFormulaAggregateNode : IComplexFormulaNode, IComplexFormulaAggregateNode { public IComplexFormulaNode Expression { get; set; } public AggregateCalculation Calculation { get; set; } public Filter? Filter { get; set; } public Dictionary>, Expression>> Links { get; } Type IComplexFormulaAggregateNode.TAggregate => typeof(TAggregate); Type IComplexFormulaAggregateNode.TResult => typeof(TResult); public ComplexFormulaAggregateNode(IComplexFormulaNode expression, AggregateCalculation calculation, Filter? filter, Dictionary>, Expression>> links) { Expression = expression; Calculation = calculation; Filter = filter; Links = links; } public ComplexFormulaAggregateNode WithFilter(Filter filter) { Filter = filter; return this; } public ComplexFormulaAggregateNode WithLink(Expression> aggLink, Expression> masterLink) { Links.Add(aggLink, masterLink); return this; } public ComplexFormulaAggregateNode WithLinks( IEnumerable>, Expression>>> links) { Links.AddRange(links); return this; } #region IComplexFormulaAggregateNode IComplexFormulaNode IComplexFormulaAggregateNode.GetExpression() { return Expression; } AggregateCalculation IComplexFormulaAggregateNode.GetCalculation() { return Calculation; } IFilter? IComplexFormulaAggregateNode.GetFilter() { return Filter; } Dictionary IComplexFormulaAggregateNode.GetLinks() { return Links.ToDictionary(x => CoreUtils.GetFullPropertyName(x.Key, "."), x => CoreUtils.GetFullPropertyName(x.Value, ".")); } #endregion } /// /// Represents an aggregate that has no links set; call to /// set the link. /// /// /// /// public class ComplexFormulaPartialAggregateNode { public IComplexFormulaNode Expression { get; set; } public AggregateCalculation Calculation { get; set; } public Filter? Filter { get; set; } public ComplexFormulaPartialAggregateNode(IComplexFormulaNode expression, AggregateCalculation calculation, Filter? filter) { Expression = expression; Calculation = calculation; Filter = filter; } public ComplexFormulaPartialAggregateNode WithFilter(Filter filter) { Filter = filter; return this; } public ComplexFormulaAggregateNode WithLink(Expression> aggLink, Expression> masterLink) { var node = new ComplexFormulaAggregateNode(Expression, Calculation, Filter, new Dictionary>, Expression>>()); node.Links.Add(aggLink, masterLink); return node; } public ComplexFormulaAggregateNode WithLinks( IEnumerable>, Expression>>> links) { return new ComplexFormulaAggregateNode(Expression, Calculation, Filter, new Dictionary>, Expression>>().AddRange(links)); } public ComplexFormulaAggregateNode WithLinks( Dictionary>, Expression>> links) { return new ComplexFormulaAggregateNode(Expression, Calculation, Filter, links); } public ComplexFormulaAggregateNode WithLinks( params KeyValuePair>, Expression>>[] links) { var node = new ComplexFormulaAggregateNode(Expression, Calculation, Filter, new Dictionary>, Expression>>()); node.Links.AddRange(links); return node; } } #endregion #region FormulaNode public interface IComplexFormulaFormulaNode : IComplexFormulaNode { Type TResult { get; } IEnumerable GetOperands(); FormulaOperator GetOperator(); } public class ComplexFormulaFormulaNode : IComplexFormulaNode, IComplexFormulaFormulaNode { public IComplexFormulaNode[] Operands { get; set; } public FormulaOperator Operator { get; set; } Type IComplexFormulaFormulaNode.TResult => typeof(TResult); public ComplexFormulaFormulaNode(IComplexFormulaNode[] operands, FormulaOperator op) { Operands = operands; Operator = op; } IEnumerable IComplexFormulaFormulaNode.GetOperands() => Operands; FormulaOperator IComplexFormulaFormulaNode.GetOperator() => Operator; } #endregion #region ConditionNode public class ComplexFormulaPartial0ConditionNode { public IComplexFormulaNode Left { get; set; } public IComplexFormulaNode Right { get; set; } public Condition Condition { get; set; } public object? Coalesce { get; set; } public ComplexFormulaPartial0ConditionNode(IComplexFormulaNode left, IComplexFormulaNode right, Condition condition, object? coalesce) { Left = left; Right = right; Condition = condition; Coalesce = coalesce; } public ComplexFormulaPartial1ConditionNode Then(IComplexFormulaNode then) { return new ComplexFormulaPartial1ConditionNode(Left, Right, Condition, then, Coalesce); } } public class ComplexFormulaPartial1ConditionNode { public IComplexFormulaNode Left { get; set; } public IComplexFormulaNode Right { get; set; } public IComplexFormulaNode True { get; set; } public Condition Condition { get; set; } public object? Coalesce { get; set; } public ComplexFormulaPartial1ConditionNode(IComplexFormulaNode left, IComplexFormulaNode right, Condition condition, IComplexFormulaNode trueValue, object? coalesce) { Left = left; Right = right; Condition = condition; True = trueValue; Coalesce = coalesce; } public ComplexFormulaConditionNode Else(IComplexFormulaNode elseValue) { return new ComplexFormulaConditionNode(Left, Right, True, elseValue, Condition, Coalesce); } } public interface IComplexFormulaConditionNode : IComplexFormulaNode { public Type TCondition { get; } public IComplexFormulaNode Left { get; } public IComplexFormulaNode Right { get; } public IComplexFormulaNode True { get; } public IComplexFormulaNode False { get; } public Condition Condition { get; } public object? Coalesce { get; } } public class ComplexFormulaConditionNode : IComplexFormulaNode, IComplexFormulaConditionNode { Type IComplexFormulaConditionNode.TCondition => typeof(TCondition); public IComplexFormulaNode Left { get; set; } public IComplexFormulaNode Right { get; set; } public IComplexFormulaNode True { get; set; } public IComplexFormulaNode False { get; set; } public Condition Condition { get; set; } public object? Coalesce { get; } public ComplexFormulaConditionNode(IComplexFormulaNode left, IComplexFormulaNode right, IComplexFormulaNode trueValue, IComplexFormulaNode falseValue, Condition condition, object? coalesce) { Left = left; Right = right; True = trueValue; False = falseValue; Condition = condition; Coalesce = coalesce; } IComplexFormulaNode IComplexFormulaConditionNode.Left => Left; IComplexFormulaNode IComplexFormulaConditionNode.Right => Right; IComplexFormulaNode IComplexFormulaConditionNode.True => True; IComplexFormulaNode IComplexFormulaConditionNode.False => False; } #endregion #region Generator + Interface public interface IComplexFormulaGenerator { IComplexFormulaNode GetFormula(); } public abstract class ComplexFormulaGenerator { public static IComplexFormulaNode Property(Expression> expression) { return new ComplexFormulaFieldNode(expression); } public static IComplexFormulaNode Formula(FormulaOperator op, params IComplexFormulaNode[] operands) { return new ComplexFormulaFormulaNode(operands, op); } public static IComplexFormulaNode Aggregate( AggregateCalculation calculation, IComplexFormulaNode expression, KeyValuePair>, Expression>>[] links, Filter? filter = null ) { return new ComplexFormulaAggregateNode(expression, calculation, filter, links.ToDictionary(x => x.Key, x => x.Value)); } public static ComplexFormulaPartialAggregateNode Aggregate( AggregateCalculation calculation, IComplexFormulaNode expression, Filter? filter = null ) { return new ComplexFormulaPartialAggregateNode(expression, calculation, filter); } public static IComplexFormulaNode Count( IComplexFormulaNode expression, KeyValuePair>, Expression>>[] links, Filter? filter = null) { return new ComplexFormulaAggregateNode(expression, AggregateCalculation.Count, filter, links.ToDictionary(x => x.Key, x => x.Value)); } public static ComplexFormulaPartialAggregateNode Count( IComplexFormulaNode expression, Filter? filter = null ) { return new ComplexFormulaPartialAggregateNode(expression, AggregateCalculation.Count, filter); } public static IComplexFormulaNode Constant(TResult constant) { return new ComplexFormulaConstantNode(constant); } public static ComplexFormulaPartial0ConditionNode If( IComplexFormulaNode left, Condition condition, IComplexFormulaNode right, object? coalesce = null) { return new ComplexFormulaPartial0ConditionNode(left, right, condition, coalesce); } } public interface IComplexFormulaGenerator { IComplexFormulaNode Property(Expression> expression); IComplexFormulaNode Formula(FormulaOperator op, params IComplexFormulaNode[] operands); IComplexFormulaNode Constant(TResult constant); IComplexFormulaNode Aggregate( AggregateCalculation calculation, Func, IComplexFormulaNode> expression, KeyValuePair>, Expression>>[] links, Filter? filter = null); ComplexFormulaPartialAggregateNode Aggregate( AggregateCalculation calculation, Func, IComplexFormulaNode> expression, Filter? filter = null); IComplexFormulaNode Count( Func, IComplexFormulaNode> expression, KeyValuePair>, Expression>>[] links, Filter? filter = null); ComplexFormulaPartialAggregateNode Count( Func, IComplexFormulaNode> expression, Filter? filter = null); ComplexFormulaPartial0ConditionNode If( Func, IComplexFormulaNode> left, Condition condition, Func, IComplexFormulaNode> right, object? coalesce); } internal class InternalComplexFormulaGenerator : IComplexFormulaGenerator { public IComplexFormulaNode Property(Expression> expression) { return ComplexFormulaGenerator.Property(expression); } public IComplexFormulaNode Formula(FormulaOperator op, params IComplexFormulaNode[] operands) { return ComplexFormulaGenerator.Formula(op, operands); } public IComplexFormulaNode Constant(TResult constant) { return ComplexFormulaGenerator.Constant(constant); } public IComplexFormulaNode Aggregate( AggregateCalculation calculation, Func, IComplexFormulaNode> expression, KeyValuePair>, Expression>>[] links, Filter? filter = null) { return ComplexFormulaGenerator.Aggregate(calculation, expression(new InternalComplexFormulaGenerator()), links, filter); } public ComplexFormulaPartialAggregateNode Aggregate( AggregateCalculation calculation, Func, IComplexFormulaNode> expression, Filter? filter = null) { return ComplexFormulaGenerator.Aggregate(calculation, expression(new InternalComplexFormulaGenerator()), filter); } public ComplexFormulaPartial0ConditionNode If( Func, IComplexFormulaNode> left, Condition condition, Func, IComplexFormulaNode> right, object? coalesce) { var generator = new InternalComplexFormulaGenerator(); return ComplexFormulaGenerator.If( left(generator), condition, right(generator), coalesce); } public IComplexFormulaNode Count(Func, IComplexFormulaNode> expression, KeyValuePair>, Expression>>[] links, Filter? filter = null) { return ComplexFormulaGenerator.Count(expression(new InternalComplexFormulaGenerator()), links, filter); } public ComplexFormulaPartialAggregateNode Count(Func, IComplexFormulaNode> expression, Filter? filter = null) { return ComplexFormulaGenerator.Count(expression(new InternalComplexFormulaGenerator()), filter); } } public abstract class ComplexFormulaGenerator : ComplexFormulaGenerator, IComplexFormulaGenerator, IComplexFormulaGenerator { public abstract IComplexFormulaNode GetFormula(); #region Internals IComplexFormulaNode IComplexFormulaGenerator.GetFormula() => GetFormula(); private readonly InternalComplexFormulaGenerator InternalGenerator = new InternalComplexFormulaGenerator(); public IComplexFormulaNode Formula(FormulaOperator op, params IComplexFormulaNode[] operands) { return ((IComplexFormulaGenerator)InternalGenerator).Formula(op, operands); } public IComplexFormulaNode Property(Expression> epxression) { return ((IComplexFormulaGenerator)InternalGenerator).Property(epxression); } public IComplexFormulaNode Constant(TResult constant) { return ((IComplexFormulaGenerator)InternalGenerator).Constant(constant); } public IComplexFormulaNode Aggregate( AggregateCalculation calculation, Func, IComplexFormulaNode> expression, KeyValuePair>, Expression>>[] links, Filter? filter = null) { return ((IComplexFormulaGenerator)InternalGenerator).Aggregate(calculation, expression, links, filter); } public ComplexFormulaPartialAggregateNode Aggregate( AggregateCalculation calculation, Func, IComplexFormulaNode> expression, Filter? filter = null) { return ((IComplexFormulaGenerator)InternalGenerator).Aggregate(calculation, expression, filter); } public ComplexFormulaPartial0ConditionNode If(Func, IComplexFormulaNode> left, Condition condition, Func, IComplexFormulaNode> right, object? coalesce = null) { return ((IComplexFormulaGenerator)InternalGenerator).If(left, condition, right, coalesce); } public IComplexFormulaNode Count(Func, IComplexFormulaNode> expression, KeyValuePair>, Expression>>[] links, Filter? filter = null) { return ((IComplexFormulaGenerator)InternalGenerator).Count(expression, links, filter); } public ComplexFormulaPartialAggregateNode Count(Func, IComplexFormulaNode> expression, Filter? filter = null) { return ((IComplexFormulaGenerator)InternalGenerator).Count(expression, filter); } #endregion } public class ComplexFormulaAttribute : Attribute { public IComplexFormulaGenerator Generator { get; } public ComplexFormulaAttribute(Type generator) { var obj = Activator.CreateInstance(generator); if(obj is IComplexFormulaGenerator g) { Generator = g; } else { throw new Exception($"{nameof(ComplexFormulaAttribute)}: {generator} is not a {typeof(IComplexFormulaGenerator)}!"); } } } #endregion #endregion #region Aggregates public enum AggregateCalculation { None, Sum, Count, Maximum, Minimum, Average, Concat } public interface ICoreAggregate { Expression> Aggregate { get; } AggregateCalculation Calculation { get; } } public abstract class CoreAggregate : ICoreAggregate { public abstract Expression> Aggregate { get; } public abstract AggregateCalculation Calculation { get; } public string GetAggregate() { return string.Join(".", Aggregate.ToString().Split('.').Skip(1)); } } public interface ICoreAggregate { Expression> Aggregate { get; } Filter? Filter { get; } Dictionary>, Expression>> Links { get; } AggregateCalculation Calculation { get; } Dictionary GetLinks(); } public abstract class CoreAggregate : ICoreAggregate { public abstract Expression> Aggregate { get; } public virtual Filter? Filter => null; public abstract Dictionary>, Expression>> Links { get; } public Dictionary GetLinks() { var result = new Dictionary(); foreach (var link in Links) { var childkey = AggregateUtils.ProcessExpression(link.Key); // String.Join(".", link.Key.ToString().Split('.').Skip(1)); var parentkey = AggregateUtils.ProcessExpression(link.Value); // String.Join(".", link.Value.ToString().Split('.').Skip(1); result[childkey] = parentkey; } return result; } public abstract AggregateCalculation Calculation { get; } public string GetAggregate() { return string.Join(".", Aggregate.ToString().Split('.').Skip(1)); } } [Obsolete] public class AggregateAttribute : Attribute { public AggregateAttribute(Type calculator) { Calculator = Activator.CreateInstance(calculator); } public object Calculator { get; } public Type Source => GetSource(); public AggregateCalculation Calculation => GetCalculation(); public string Aggregate => GetAggregate(); public Dictionary Links => GetLinks(); public IFilter? Filter => GetFilter(); #region Internal (Reflection) functions private Type GetSource() { var intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,,>)); if (intf != null) return intf.GenericTypeArguments[1]; intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,>)); if (intf != null) return intf.GenericTypeArguments[0]; throw new Exception("Unable to Locate Type Information for Aggregate"); } private string GetAggregate() { var intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,,>)); if (intf == null) { intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,>)); } if (intf != null) { var prop = intf.GetProperty("Aggregate"); if (prop != null) { var obj = prop.GetValue(Calculator); if (obj != null) { var expr = obj as Expression; if (expr != null) return AggregateUtils.ProcessExpression(expr); } } } return ""; } private Dictionary GetLinks() { var intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,,>)); if (intf != null) { var method = intf.GetMethod("GetLinks"); if (method != null) { var dict = method.Invoke(Calculator, new object[] { }); return (dict as Dictionary)!; } } return new Dictionary(); } private AggregateCalculation GetCalculation() { var intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,,>)); if (intf == null) intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,>)); if (intf != null) { var prop = intf.GetProperty("Calculation"); if (prop != null) return (AggregateCalculation)prop.GetValue(Calculator); } return AggregateCalculation.None; } private IFilter? GetFilter() { var intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICoreAggregate<,,>)); if (intf != null) { var prop = intf.GetProperty("Filter"); if (prop != null) return prop.GetValue(Calculator) as IFilter; } return null; } #endregion } #endregion #region Formulas public enum FormulaType { Virtual, Permanent } public enum FormulaOperator { None, /// /// Add the values together. If no values are provided, the result is 0 /// Add, /// /// Subtract all values from the first value. If no values are /// provided, the result is 0; if only one value is provided, the /// result is the negation of the input. /// Subtract, /// /// Multiply the values together. If no values are provided, the result is 1 /// Multiply, /// /// Divide all values from the first value. If no values are /// provided, the result is 1; if only one value is provided, the /// result is the reciprocal of the input. /// Divide, /// /// Take the minimum of all values. /// Minumum, /// /// Take the maximum of all values. /// Maximum, [Obsolete] Constant, /// /// Formats all the operands using the first string /// Format } public interface IFormula { Expression> Value { get; } Expression>[] Modifiers { get; } FormulaOperator Operator { get; } FormulaType Type { get; } } public interface IFormula { String Value { get; } String[] Modifiers { get; } FormulaOperator Operator { get; } FormulaType Type { get; } } [Obsolete] public class FormulaAttribute : Attribute, IFormula { public FormulaAttribute(Type calculator) { Calculator = Activator.CreateInstance(calculator); } public object Calculator { get; } public string Value => GetExpressionName("Value"); public string[] Modifiers => GetExpressionNames("Modifiers"); public FormulaOperator Operator => GetFormulaOperator(); public FormulaType Type => GetFormulaType(); #region Internal (Reflection) functions private FormulaOperator GetFormulaOperator() { var intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IFormula<,>)); if (intf != null) { var prop = intf.GetProperty("Operator"); if (prop != null) return (FormulaOperator)prop.GetValue(Calculator); } return FormulaOperator.None; } private FormulaType GetFormulaType() { var intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IFormula<,>)); if (intf != null) { var prop = intf.GetProperty("Type"); if (prop != null) return (FormulaType)prop.GetValue(Calculator); } return FormulaType.Virtual; } private string GetExpressionName(string property) { var intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IFormula<,>)); if (intf != null) { var prop = intf.GetProperty(property); if (prop != null) { if(prop.GetValue(Calculator) is LambdaExpression expr) { var result = AggregateUtils.ProcessExpression(expr.Body); return result; } } } return ""; } private string[] GetExpressionNames(string property) { var intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IFormula<,>)); if (intf != null) { var prop = intf.GetProperty(property); if (prop != null) { if(prop.GetValue(Calculator) is LambdaExpression[] expressions) { var result = new List(); foreach (var expression in expressions) result.Add(AggregateUtils.ProcessExpression(expression.Body)); return result.ToArray(); } //return expressions.Select(x => x.Body is ConstantExpression ? ProcessConstantExpression(x.Body) : String.Join(".", RemoveConvert(x.Body).Split('.').Skip(1))).ToArray(); } } return new string[] { }; } #endregion } #endregion #region Conditions public enum Condition { None, Equals, NotEqual, GreaterThan, GreaterThanOrEqualTo, LessThan, LessThanOrEqualTo } public enum ConditionType { Virtual, Permanent } public interface ICondition { Expression> Left { get; } Condition Condition { get; } Expression> Right { get; } Expression> True { get; } Expression> False { get; } ConditionType Type { get; } } [Obsolete] public class ConditionAttribute : Attribute { public ConditionAttribute(Type calculator) { Calculator = Activator.CreateInstance(calculator); } public object Calculator { get; } public string Left => GetExpressionName("Left"); public Condition Condition => GetCondition(); public string Right => GetExpressionName("Right"); public string True => GetExpressionName("True"); public string False => GetExpressionName("False"); public ConditionType Type => GetConditionType(); #region Internal (Reflection) functions private Condition GetCondition() { var intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICondition<,,>)); if (intf != null) { var prop = intf.GetProperty("Condition"); if (prop != null) return (Condition)prop.GetValue(Calculator); } return Condition.None; } private ConditionType GetConditionType() { var intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICondition<,,>)); if (intf != null) { var prop = intf.GetProperty("Type"); if (prop != null) return (ConditionType)prop.GetValue(Calculator); } return ConditionType.Virtual; } private string GetExpressionName(string property) { var intf = Calculator.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICondition<,,>)); if (intf != null) { var prop = intf.GetProperty(property); if (prop?.GetValue(Calculator) is LambdaExpression expr) { return AggregateUtils.ProcessExpression(expr.Body); } } return ""; } #endregion } #endregion #region ChildEntity public interface IChildEntityDefinition { string ParentColumn { get; } Type EntityType { get; } IFilter? Filter { get; } ISortOrder? Sort { get; } } public interface IChildEntityDefinition : IChildEntityDefinition where TEntity : Entity { new Filter? Filter { get; } new SortOrder? Sort { get; } Expression> Parent { get; } Type IChildEntityDefinition.EntityType => typeof(TEntity); IFilter? IChildEntityDefinition.Filter => Filter; ISortOrder? IChildEntityDefinition.Sort => Sort; string IChildEntityDefinition.ParentColumn => CoreUtils.GetFullPropertyName(Parent, "."); } public class ChildEntityAttribute : Attribute { public IChildEntityDefinition Calculator { get; set; } public ChildEntityAttribute(Type definition) { Calculator = (Activator.CreateInstance(definition) as IChildEntityDefinition) ?? throw new Exception($"{definition} is not an {nameof(IChildEntityDefinition)}"); } } #endregion }