persistence/DD.Persistence.Database.Postgres/Helpers/FilterBuilder.cs

138 lines
6.5 KiB
C#
Raw Normal View History

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<TEntity>? BuildFilter<TEntity>(this DataScheme dataScheme, TNode root)
where TEntity : IValuesItem
{
var result = dataScheme.BuildSpecificationByNextNode<TEntity>(root);
return result;
}
private static ISpecification<TEntity>? BuildSpecificationByNextNode<TEntity>(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<ISpecification<TEntity>?>(
v =>
{
var leftSpecification = dataScheme.BuildSpecificationByNextNode<TEntity>(v.Left);
var rigthSpecification = dataScheme.BuildSpecificationByNextNode<TEntity>(v.Rigth);
if (leftSpecification is null)
return rigthSpecification;
if (rigthSpecification is null)
return leftSpecification;
ISpecification<TEntity>? 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<TEntity>? result = null;
switch (type)
{
case PropTypeEnum.String:
var stringValue = Convert.ToString(t.Value);
switch (t.Operation)
{
case OperationEnum.Equal:
result = new ValueEqaulSpecification<TEntity>(keyIndex, stringValue);
break;
case OperationEnum.NotEqual:
result = new ValueNotEqaulSpecification<TEntity>(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<TEntity>(keyIndex, doubleValue);
break;
case OperationEnum.NotEqual:
result = new ValueNotEqaulSpecification<TEntity>(keyIndex, doubleValue);
break;
case OperationEnum.Greate:
result = new ValueGreateSpecification<TEntity>(keyIndex, doubleValue);
break;
case OperationEnum.GreateOrEqual:
result = new ValueGreateOrEqualSpecification<TEntity>(keyIndex, doubleValue);
break;
case OperationEnum.Less:
result = new ValueLessSpecification<TEntity>(keyIndex, doubleValue);
break;
case OperationEnum.LessOrEqual:
result = new ValueLessOrEqualSpecification<TEntity>(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<TEntity>(keyIndex, dateTimeValue);
break;
case OperationEnum.NotEqual:
result = new ValueNotEqaulSpecification<TEntity>(keyIndex, dateTimeValue);
break;
case OperationEnum.Greate:
result = new ValueGreateSpecification<TEntity>(keyIndex, dateTimeValue);
break;
case OperationEnum.GreateOrEqual:
result = new ValueGreateOrEqualSpecification<TEntity>(keyIndex, dateTimeValue);
break;
case OperationEnum.Less:
result = new ValueLessSpecification<TEntity>(keyIndex, dateTimeValue);
break;
case OperationEnum.LessOrEqual:
result = new ValueLessOrEqualSpecification<TEntity>(keyIndex, dateTimeValue);
break;
}
break;
}
return result;
}
);
var result = node.AcceptVisitor(visitor);
return result;
}
}