persistence/DD.Persistence.Database.Postgres/Extensions/SpecificationExtensions.cs

84 lines
3.0 KiB
C#
Raw Normal View History

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<T> Or<T>(this ISpecification<T> spec, ISpecification<T> otherSpec)
{
var newSpec = new EmptySpecification<T>();
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<Func<T, bool>>(orExpression, parameter);
newSpec.Query.Where(lambdaExpr);
return newSpec;
}
// ToDo: Рефакторинг
public static ISpecification<T> And<T>(this ISpecification<T> spec, ISpecification<T> otherSpec)
{
var newSpec = new EmptySpecification<T>();
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<Func<T, bool>>(andExpression, parameter);
newSpec.Query.Where(lambdaExpr);
return newSpec;
}
public static Expression? CombineWhereExpressions<T>(IEnumerable<WhereExpressionInfo<T>> 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;
}