微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

EF Core,在任何条件下附加到谓词构建器

如何解决EF Core,在任何条件下附加到谓词构建器

我正在尝试在以下代码的 then Any 子句中合并两个谓词,但我找不到方法来做到这一点。

private static Expression<Func<Order,bool>> BuildWhereExpression(DataFilterOrder filter,AppDbContext dbContext)
{

var predicate = PredicateBuilder.True<Order>();

var confirmationPredicate = PredicateBuilder.True<HConfirmation>();

if (!string.IsNullOrWhiteSpace(filter.ConfirmationNumber))
{
  confirmationPredicate = confirmationPredicate.And(r =>
  r.Confirmation.Document.Number == filter.ConfirmationNumber);
}
if (filter.ConfirmationDateFrom != null)
{
  confirmationPredicate = confirmationPredicate.And(r =>
  r.Confirmation.Document.Date >= filter.ConfirmationDateFrom);
}
.....


predicate = predicate.And(o =>
   dbContext.Confirmations
   .Join(
     dbContext.DocumentHierarchies,c => c.DocumentId,h => h.ChildDocumentId,(c,h) => new HConfirmation { Confirmation = c,Hierarchy = h })
   .Any(r => r.Hierarchy.ParentDocumentId == o.DocumentId && 
   ???confirmationPredicate???)

  return predicate;
}
....

// called by

  var wherePredicate = BuildWhereExpression(filter,dbContext);


  var list = await dbContext.Orders
    .Where(wherePredicate)
    .ToListAsync();

有什么帮助吗?非常感谢。

PredicateBuilder 类:

public static class PredicateBuilder
    {

        // Creates a predicate that evaluates to true.        
        public static Expression<Func<T,bool>> True<T>() { return param => true; }

        // Creates a predicate that evaluates to false.        
        public static Expression<Func<T,bool>> False<T>() { return param => false; }

        // Creates a predicate expression from the specified lambda expression.        
        public static Expression<Func<T,bool>> Create<T>(Expression<Func<T,bool>> predicate) { return predicate; }
        public static Expression<Func<T,bool>> Create1<T,K>(Expression<Func<T,bool>> predicate,K obj) { return predicate; }

        // Combines the first predicate with the second using the logical "and".        
        public static Expression<Func<T,bool>> And<T>(this Expression<Func<T,bool>> first,Expression<Func<T,bool>> second)
        {
            return first.Compose(second,Expression.AndAlso);
        }

        // Combines the first predicate with the second using the logical "or".        
        public static Expression<Func<T,bool>> Or<T>(this Expression<Func<T,Expression.OrElse);
        }

        // Negates the predicate.        
        public static Expression<Func<T,bool>> Not<T>(this Expression<Func<T,bool>> expression)
        {
            var negated = Expression.Not(expression.Body);
            return Expression.Lambda<Func<T,bool>>(negated,expression.Parameters);
        }

        // Combines the first expression with the second using the specified merge function.        
        static Expression<T> Compose<T>(this Expression<T> first,Expression<T> second,Func<Expression,Expression,Expression> merge)
        {
            // zip parameters (map from parameters of second to parameters of first)
            var map = first.Parameters
                .Select((f,i) => new { f,s = second.Parameters[i] })
                .ToDictionary(p => p.s,p => p.f);

            // replace parameters in the second lambda expression with the parameters in the first
            var secondBody = ParameterRebinder.ReplaceParameters(map,second.Body);

            // create a merged lambda expression with parameters from the first expression
            return Expression.Lambda<T>(merge(first.Body,secondBody),first.Parameters);
        }

        class ParameterRebinder : ExpressionVisitor
        {
            readonly Dictionary<ParameterExpression,ParameterExpression> map;

            ParameterRebinder(Dictionary<ParameterExpression,ParameterExpression> map)
            {
                this.map = map ?? new Dictionary<ParameterExpression,ParameterExpression>();
            }

            public static Expression ReplaceParameters(Dictionary<ParameterExpression,ParameterExpression> map,Expression exp)
            {
                return new ParameterRebinder(map).Visit(exp);
            }

            protected override Expression VisitParameter(ParameterExpression p)
            {
                ParameterExpression replacement;
                if (map.TryGetValue(p,out replacement))
                {
                    p = replacement;
                }
                return base.VisitParameter(p);
            }
        }
    }

解决方法

好吧,试图简化解决方案,但看起来需要动态构建 Any 部分。抱歉没有经过测试,这里可能会出现一些小错误。

private static Expression<Func<Order,bool>> BuildWhereExpression(DataFilterOrder filter,AppDbContext dbContext)
{
    var predicate = PredicateBuilder.True<Order>();

    var confirmationPredicate = PredicateBuilder.True<HConfirmation>();

    if (!string.IsNullOrWhiteSpace(filter.ConfirmationNumber))
    {
        confirmationPredicate = confirmationPredicate.And(r =>
        r.Confirmation.Document.Number == filter.ConfirmationNumber);
    }
    if (filter.ConfirmationDateFrom != null)
    {
        confirmationPredicate = confirmationPredicate.And(r =>
        r.Confirmation.Document.Date >= filter.ConfirmationDateFrom);
    }
    .....

    // we can write this query separately
    var confirmations = dbContext.Confirmations
        .Join(dbContext.DocumentHierarchies,c => c.DocumentId,h => h.ChildDocumentId,(c,h) => new HConfirmation { Confirmation = c,Hierarchy = h }
        );

    var orderParam = Expression.Parameter(typeof(Order),"o");
    var hConfirmationParam = Expression.Parameter(typeof(HConfirmation),"r");

    // r.Hierarchy.ParentDocumentId == o.DocumentId
    var anyPredicate = (Expression)Expression.Equal(Expression.Property(Expression.Property(hConfirmationParam,"Hierarchy"),"ParentDocumentId"),Expression.Property(orderParam,"DocumentId"));

    // r.Confirmation
    var confirmationAccess = Expression.Property(hConfirmationParam,"Confirmation");
    // correcting confirmation predicate
    var confirmationPredicateCorrected = ExpressionReplacer.GetBody(confirmationPredicate,confirmationAccess);

    // r.Hierarchy.ParentDocumentId == o.DocumentId && confirmationPredicateCorrected
    anyPredicate = Expression.AndAlso(anyPredicate,confirmationPredicateCorrected);
    // r => r.Hierarchy.ParentDocumentId == o.DocumentId && confirmationPredicateCorrected
    var anyLambda = Expression.Lambda(anyPredicate,hConfirmationParam);

    var anyCall = Expression.Call(typeof(Queryable),"Any",new [] { typeof(HConfirmation) },confirmations.Expression,Expression.Quote(anyLambda));

    var additionalPredicate = Expression.Lambda<Func<Order,bool>>(anyCall,orderParam);

    predicate = predicate.And(additionalPredicate);

    return predicate;
}

好吧,需要额外的帮助类:

public class ExpressionReplacer : ExpressionVisitor
{
    readonly IDictionary<Expression,Expression> _replaceMap;

    public ExpressionReplacer(IDictionary<Expression,Expression> replaceMap)
    {
        _replaceMap = replaceMap ?? throw new ArgumentNullException(nameof(replaceMap));
    }

    public override Expression Visit(Expression exp)
    {
        if (exp != null && _replaceMap.TryGetValue(exp,out var replacement))
            return replacement;
        return base.Visit(exp);
    }

    public static Expression Replace(Expression expr,Expression toReplace,Expression toExpr)
    {
        return new ExpressionReplacer(new Dictionary<Expression,Expression> { { toReplace,toExpr } }).Visit(expr);
    }

    public static Expression Replace(Expression expr,IDictionary<Expression,Expression> replaceMap)
    {
        return new ExpressionReplacer(replaceMap).Visit(expr);
    }

    public static Expression GetBody(LambdaExpression lambda,params Expression[] toReplace)
    {
        if (lambda.Parameters.Count != toReplace.Length)
            throw new InvalidOperationException();

        return new ExpressionReplacer(Enumerable.Range(0,lambda.Parameters.Count)
            .ToDictionary(i => (Expression) lambda.Parameters[i],i => toReplace[i])).Visit(lambda.Body);
    }
}

强烈推荐这个用于表达式树可视化和调试的扩展:https://marketplace.visualstudio.com/items?itemName=vs-publisher-1232914.ReadableExpressionsVisualizers

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。