The source code of this PredicateBuilder as following:
/// <summary> /// Enables the efficient, dynamic composition of query predicates. /// </summary> public static class PredicateBuilder { /// <summary> /// Creates a predicate that evaluates to true. /// </summary> public static Expression<Func<T, bool>> True<T>() { return param => true; } /// <summary> /// Creates a predicate that evaluates to false. /// </summary> public static Expression<Func<T, bool>> False<T>() { return param => false; } /// <summary> /// Creates a predicate expression from the specified lambda expression. /// </summary> public static Expression<Func<T, bool>> Create<T>( Expression<Func<T, bool>> predicate) { return predicate; } /// <summary> /// Combines the first predicate with the second using the logical "and". /// </summary> 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); } /// <summary> /// Combines the first predicate with the second using the logical "or". /// </summary> 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); } /// <summary> /// Negates the predicate. /// </summary> 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); } /// <summary> /// Combines the first expression with the second using the specified merge function. /// </summary> 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) { _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); } } }
But please stay on, we have a problem if we use this PredicateBulder with dynamic filter that is same as (A OR B) AND (X OR Y).
When you try to build this filter, at run-time, you it throws an exception message as following:
The parameter 'f' was not bound in the specified LINQ to Entities query expression.
To solve the above issue, we must use Expand method to combine between PredicateBuilder expressions.
And here is the sample for this problem:
Sample in SQL syntax:
SELECT * FROM dbo.Customers c WHERE (c.FirstName LIKE '%A%' OR c.LastName LIKE '%B%') AND (c.Birthday = '1998-01-01' OR c.Birthday = '1999-12-31')
And here is in C# Statements:
using (var db = new CustomerContext()) { //With OR block, you must use FALSE var wBlockOr1 = PredicateBuilder.False(); wBlockOr1 = wBlockOr1.Or(c => c.FirstName.Contains("A")); wBlockOr1 = wBlockOr1.Or(c => c.LastName.Contains("B")); var wBlockOr2 = PredicateBuilder.False(); wBlockOr1 = wBlockOr1.Or( c => c.Birthday == new DateTime(1998, 1, 1)); wBlockOr1 = wBlockOr1.Or( c => c.Birthday == new DateTime(1999, 12, 31)); //With AND block, you must use TRUE var wAndBlock = PredicateBuilder.True(); //Combine between Or blocks wAndBlock = wAndBlock.And(wBlockOr1.Expand()); wAndBlock = wAndBlock.And(wBlockOr2.Expand()); var customers = db.Customers.AsExpandable().Where(wAndBlock); }
Hope that this article help you to solve the problem on LINQ to Entities.
No comments:
Post a Comment