如何解决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 举报,一经查实,本站将立刻删除。