using Ardalis.Specification; using DD.Persistence.Database.Entity; using DD.Persistence.Database.EntityAbstractions; using DD.Persistence.Database.Postgres.Extensions; using DD.Persistence.Database.Specifications.ValuesItem; using DD.Persistence.Filter.Models.Abstractions; using DD.Persistence.Filter.Models.Enumerations; using DD.Persistence.Filter.Visitors; using DD.Persistence.Models; namespace DD.Persistence.Database.Postgres.Helpers; public static class FilterBuilder { public static ISpecification? BuildFilter(this DataScheme dataScheme, TNode root) where TEntity : IValuesItem { var result = dataScheme.BuildSpecificationByNextNode(root); return result; } private static ISpecification? BuildSpecificationByNextNode(this DataScheme dataScheme, TNode node) where TEntity : IValuesItem { var propIndexMap = dataScheme.PropNames .Select((name, index) => new { name, index }) .ToDictionary(x => x.name, x => x.index); var visitor = new NodeVisitor?>( v => { var leftSpecification = dataScheme.BuildSpecificationByNextNode(v.Left); var rigthSpecification = dataScheme.BuildSpecificationByNextNode(v.Rigth); if (leftSpecification is null) return rigthSpecification; if (rigthSpecification is null) return leftSpecification; ISpecification? result = null; switch (v.Operation) { case OperationEnum.And: result = leftSpecification.And(rigthSpecification); break; case OperationEnum.Or: result = leftSpecification.Or(rigthSpecification); break; } return result; }, t => { int keyIndex; if (!propIndexMap.TryGetValue(t.PropName, out keyIndex)) throw new ArgumentException($"Свойство {t.PropName} не найдено в схеме данных"); var type = dataScheme.PropTypes[keyIndex]; ISpecification? result = null; switch (type) { case PropTypeEnum.String: var stringValue = Convert.ToString(t.Value); switch (t.Operation) { case OperationEnum.Equal: result = new ValueEqaulSpecification(keyIndex, stringValue); break; case OperationEnum.NotEqual: result = new ValueNotEqaulSpecification(keyIndex, stringValue); break; } break; case PropTypeEnum.Double: var doubleValue = Convert.ToDouble(t.Value); switch (t.Operation) // ToDo: можно схлопнуть в один Generic - метод, где TValue : struct // Но в таком случае придётся продумать аналогичное решение на уровне спецификаций { case OperationEnum.Equal: result = new ValueEqaulSpecification(keyIndex, doubleValue); break; case OperationEnum.NotEqual: result = new ValueNotEqaulSpecification(keyIndex, doubleValue); break; case OperationEnum.Greate: result = new ValueGreateSpecification(keyIndex, doubleValue); break; case OperationEnum.GreateOrEqual: result = new ValueGreateOrEqualSpecification(keyIndex, doubleValue); break; case OperationEnum.Less: result = new ValueLessSpecification(keyIndex, doubleValue); break; case OperationEnum.LessOrEqual: result = new ValueLessOrEqualSpecification(keyIndex, doubleValue); break; } break; case PropTypeEnum.DateTime: stringValue = Convert.ToString(t.Value); DateTimeOffset? dateTimeValue = string.IsNullOrEmpty(stringValue) ? null : DateTimeOffset.Parse(stringValue); switch (t.Operation) { case OperationEnum.Equal: result = new ValueEqaulSpecification(keyIndex, dateTimeValue); break; case OperationEnum.NotEqual: result = new ValueNotEqaulSpecification(keyIndex, dateTimeValue); break; case OperationEnum.Greate: result = new ValueGreateSpecification(keyIndex, dateTimeValue); break; case OperationEnum.GreateOrEqual: result = new ValueGreateOrEqualSpecification(keyIndex, dateTimeValue); break; case OperationEnum.Less: result = new ValueLessSpecification(keyIndex, dateTimeValue); break; case OperationEnum.LessOrEqual: result = new ValueLessOrEqualSpecification(keyIndex, dateTimeValue); break; } break; } return result; } ); var result = node.AcceptVisitor(visitor); return result; } }