Linq EF Core,在任何条件下追加到谓词生成器
我试图在下面代码的任意子句中合并两个谓词,但我找不到一种方法Linq EF Core,在任何条件下追加到谓词生成器,linq,entity-framework-core,predicatebuilder,Linq,Entity Framework Core,Predicatebuilder,我试图在下面代码的任意子句中合并两个谓词,但我找不到一种方法 private static Expression<Func<Order, bool>> BuildWhereExpression(DataFilterOrder filter, AppDbContext dbContext) { var predicate = PredicateBuilder.True<Order>(); var confirmationPredicate = Predica
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();
嗯,试图简化解决方案,但似乎需要动态构建
任何零件。对不起,没有测试,这里可能有一些小错误
私有静态表达式BuildWhereExpression(DataFilterOrder筛选器,AppDbContext dbContext)
{
var predicate=PredicateBuilder.True();
var-confirmationPredicate=PredicateBuilder.True();
如果(!string.IsNullOrWhiteSpace(filter.ConfirmationNumber))
{
确认谓词=确认谓词。和(r=>
r、 Confirmation.Document.Number==filter.ConfirmationNumber);
}
if(filter.ConfirmationDateFrom!=null)
{
确认谓词=确认谓词。和(r=>
r、 Confirmation.Document.Date>=filter.ConfirmationDateFrom);
}
.....
//我们可以单独编写这个查询
var confirmations=dbContext.confirmations
.Join(dbContext.documentHierarchys,
c=>c.DocumentId,
h=>h.ChildDocumentId,
(c,h)=>新的HConfirmation{Confirmation=c,Hierarchy=h}
);
var orderParam=表达式参数(typeof(Order),“o”);
var hconfimationparam=表达式参数(typeof(hconfimation),“r”);
//r.Hierarchy.ParentDocumentId==o.DocumentId
var anyPredicate=(Expression)Expression.Equal(Expression.Property)(Expression.Property(hconfimationparam,“Hierarchy”),“ParentDocumentId”),
Expression.Property(orderParam,“DocumentId”);
//r.确认
var confirmationAccess=Expression.Property(hConfirmationParam,“Confirmation”);
//纠正确认谓词
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,hconfimationparam);
var anyCall=Expression.Call(typeof(Queryable),“Any”,new[]{typeof(hconfimation)},confirmations.Expression,Expression.Quote(anyLambda));
var additionalPredicate=Expression.Lambda(anyCall,orderParam);
谓词=谓词和(附加谓词);
返回谓词;
}
无论如何,需要额外的助手类:
公共类ExpressionReplacer:ExpressionVisitor
{
只读IDictionary _replaceMap;
public ExpressionReplacer(IDictionary replaceMap)
{
_replaceMap=replaceMap??抛出新的ArgumentNullException(nameof(replaceMap));
}
公共重写表达式访问(表达式扩展)
{
if(exp!=null&&u replaceMap.TryGetValue(exp,out var replacement))
退换货;
返回基地访问(exp);
}
公共静态表达式替换(表达式expr、表达式toReplace、表达式toExpr)
{
返回新的ExpressionReplacer(新字典{{toReplace,toExpr}});
}
公共静态表达式替换(表达式表达式表达式、IDictionary替换映射)
{
返回新的ExpressionReplacer(replaceMap)。访问(expr);
}
公共静态表达式GetBody(LambdaExpression lambda,params Expression[]toReplace)
{
if(lambda.Parameters.Count!=toReplace.Length)
抛出新的InvalidOperationException();
返回新的ExpressionReplacer(Enumerable.Range(0,lambda.Parameters.Count)
.ToDictionary(i=>(表达式)lambda.Parameters[i],i=>toReplace[i]).Visit(lambda.Body);
}
}
强烈建议将此扩展用于表达式树可视化和调试:Any(confirmationPredicate.and(r=>r.Hierarchy.ParentDocumentId==o.DocumentId))
@IvanStoev引发异常:System.InvalidCastException:无法将类型为“System.Linq.Expressions.InstanceMethodCallExpression1”的对象强制转换为类型为“System.Linq.Expressions.LambdaExpression”。堆栈跟踪:ExpressionExtensions.UnwaplambFromQuote(表达式表达式)不太容易,但可行。哪个是调用方法(dbContext等)的签名?@SvyatoslavDanyliv已更新。
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, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, 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);
}
}
}