using Ardalis.Specification; using DD.Persistence.Database.Specifications; using System.Linq.Expressions; namespace DD.Persistence.Database.Postgres.Extensions; // ToDo: рассмотреть возможность вынести логику в спецификации public static class SpecificationExtensions { public static ISpecification Or(this ISpecification spec, ISpecification otherSpec) { var newSpec = new EmptySpecification(); var parameter = Expression.Parameter(typeof(T), "x"); var exprSpec1 = CombineWhereExpressions(spec.WhereExpressions, parameter); var exprSpec2 = CombineWhereExpressions(otherSpec.WhereExpressions, parameter); Expression? orExpression = exprSpec1 is not null && exprSpec2 is not null ? Expression.OrElse(exprSpec1, exprSpec2) : exprSpec1 ?? exprSpec2; if (orExpression is null) return newSpec; var lambdaExpr = Expression.Lambda>(orExpression, parameter); newSpec.Query.Where(lambdaExpr); return newSpec; } // ToDo: Рефакторинг public static ISpecification And(this ISpecification spec, ISpecification otherSpec) { var newSpec = new EmptySpecification(); var parameter = Expression.Parameter(typeof(T), "x"); var exprSpec1 = CombineWhereExpressions(spec.WhereExpressions, parameter); var exprSpec2 = CombineWhereExpressions(otherSpec.WhereExpressions, parameter); Expression? andExpression = exprSpec1 is not null && exprSpec2 is not null ? Expression.AndAlso(exprSpec1, exprSpec2) : exprSpec1 ?? exprSpec2; if (andExpression is null) return newSpec; var lambdaExpr = Expression.Lambda>(andExpression, parameter); newSpec.Query.Where(lambdaExpr); return newSpec; } public static Expression? CombineWhereExpressions(IEnumerable> whereExpressions, ParameterExpression parameter) { Expression? newExpr = null; foreach (var where in whereExpressions) { var expr = ParameterReplacerVisitor.Replace(where.Filter.Body, where.Filter.Parameters[0], parameter); newExpr = newExpr is null ? expr : Expression.AndAlso(newExpr, expr); } return newExpr; } } public class ParameterReplacerVisitor : ExpressionVisitor { private readonly Expression _newExpression; private readonly ParameterExpression _oldParameter; private ParameterReplacerVisitor(ParameterExpression oldParameter, Expression newExpression) { _oldParameter = oldParameter; _newExpression = newExpression; } internal static Expression Replace(Expression expression, ParameterExpression oldParameter, Expression newExpression) => new ParameterReplacerVisitor(oldParameter, newExpression).Visit(expression); protected override Expression VisitParameter(ParameterExpression p) => p == _oldParameter ? _newExpression : p; }