How to Append to an expression

后端 未结 8 1515
清歌不尽
清歌不尽 2020-12-05 01:46

Based on my question from yesterday:

if I had to append to my existing \'where\' expression, how would i append?

Expression

        
相关标签:
8条回答
  • 2020-12-05 02:11

    If you encounter a similar problem, you can find all possible solutions in this great topic. Or just use PredicateBuilder is awesome helper for this poporse.

    var predicate = PredicateBuilder.True<Client>();
    
    if (filterByClientFName)
    {
        predicate = predicate.And(c => c.ClientFName == searchForClientFName);
    }
    
    if (filterByClientLName)
    {
            predicate = predicate.And(c => c.ClientLName == searchForClientLName);
    }
    
    var result = context.Clients.Where(predicate).ToArray();
    

    It is some builder implementation.

    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; }
    
            // 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);
                }
            }
        }
    
    0 讨论(0)
  • 2020-12-05 02:14

    Take a look at Predicate Builder, I believe this might work for you.

    0 讨论(0)
  • 2020-12-05 02:16

    Much easier and elegant solution from 2020 :)

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.And);
        }
    

    Works for IQueryable.

    0 讨论(0)
  • 2020-12-05 02:16

    Or something to add to Josh (Put it in my bag of tricks):

    public static IQueryable<TSource> ObjectFilter<TSource>(this TSource SearchObject, List<Predicate<TSource>> andCriteria, List<Predicate<TSource>> orCriteria) where TSource : IQueryable<TSource>
            {
                //Yeah :)
                Expression<Func<TSource, bool>> ObjectWhere = O => andCriteria.All(pred => pred(O)) && orCriteria.Any(pred => pred(O));
                return SearchObject.Where<TSource>(ObjectWhere);
            }
    
    0 讨论(0)
  • 2020-12-05 02:21

    This is a complex scenario. You are almost building your own query engine on top of LINQ. JaredPar's solution (where did it go?) is great if you want a logical AND between all of your criteria, but that may not always be the case.

    When I was wrangling with this in one of my project recently, I created two Lists:

    List<Predicate<T>> andCriteria;
    List<Predicate<T>> orCriteria;
    

    (In this case, T is Client, for you)

    I would populate the Lists with predicates that I want to be true. For instance,

    decimal salRequirement = 50000.00;
    andCriteria.Add(c => c.Salary > salRequirement);
    orCriteria.Add(c => c.IsMarried);
    

    Then, I would check against all the criteria in the Lists in my Where clause. For instance:

    Expression<Func<Client, bool>> clientWhere =
        c => andCriteria.All(pred => pred(c) ) && orCriteria.Any(pred => pred(c) );
    

    This could also be done with a for-loop for readability's sake. Remember to use the correct order of operations when applying your OR and AND clauses.

    0 讨论(0)
  • 2020-12-05 02:28

    It´s not exactly the answer for your question, but, I was looking for the same thing you are, and then I've found a better answer to my question.

    Instead of building a dynamic Expression, you could retrieve the IQueryable and then filter what you want like this:

    var customers = CustomerRepository.AllEntities();
    
    if (!forename.IsNullOrEmpty())
        customers = customers.Where(p => p.Forename == forename);
    if (!familyname.IsNullOrEmpty())
        customers = customers.Where(p => p.FamilyNames.Any(n => n.Name==familyname));
    if (dob.HasValue)
        customers = customers.Where(p => p.DOB == dob);
    

    Note: I was concerned about executing more then one ".Where" statement because I was afraid this would generate more than one query in the DataBase, or because I would have to retrive all records and then filter them, but this is not true, Linq dynamic generate just one query only when you call .ToList() method.

    Here you can see original question that I've took the example from.

    0 讨论(0)
提交回复
热议问题