How to write string.Contains(someText) in expression Tree

前端 未结 2 1557
离开以前
离开以前 2021-01-21 06:19

This is the tutorial I\'m following to learn Expression Tree.

I\'ve more than 35 columns to display, but the user can chose to display 10 columns at once. So one the use

相关标签:
2条回答
  • 2021-01-21 07:10

    You are pretty close, except constructing the call of Contains does not have a right side:

    Expression col = Expression.Property(pe, columnName);
    Expression contains = Expression.Call(
        pe
    ,   typeof(string).GetMethod("Contains") // Make a static field out of this
    ,   Expression.Constant(searchText)      // Prepare a shared object before the loop
    );
    

    Once you have your call expressions, combine them with OrElse to produce the body of your lambda. You can do it with loops, or you can use LINQ:

    private static readonly MethodInfo Contains = typeof(string).GetMethod(nameof(string.Contains));
    
    public static Expression<Func<Student,bool>> SearchPredicate(IEnumerable<string> properties, string searchText) {
        var param = Expression.Parameter(typeof(Student));
        var search = Expression.Constant(searchText);
        var components = properties
            .Select(propName => Expression.Call(Expression.Property(param, propName), Contains, search))
            .Cast<Expression>()
            .ToList();
        // This is the part that you were missing
        var body = components
            .Skip(1)
            .Aggregate(components[0], Expression.OrElse);
        return Expression.Lambda<Func<Student, bool>>(body, param);
    }
    
    0 讨论(0)
  • 2021-01-21 07:11

    I like the PredicateBuilder class for stuff like this scenario:

    using System;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Collections.Generic;
    
    public static class PredicateBuilder
    {
      public static Expression<Func<T, bool>> True<T> ()  { return f => true;  }
      public static Expression<Func<T, bool>> False<T> () { return f => false; }
    
      public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
                                                          Expression<Func<T, bool>> expr2)
      {
        var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
        return Expression.Lambda<Func<T, bool>>
              (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
      }
    
      public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
                                                           Expression<Func<T, bool>> expr2)
      {
        var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
        return Expression.Lambda<Func<T, bool>>
              (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
      }
    }
    

    Code using this would look like:

    var predicate = PredicateBuilder.True<Student>().And(i=>i.Id==id);
    if(!string.IsNullOrEmpty(searchText))
    {
        if (firstNameColumnVisible) {
           predicate = predicate.And(i=>i.FirstName.Contains(searchText));
        }
        if (lastNameColumnVisible) {
           predicate = predicate.And(i=>i.LastName.Contains(searchText));
        }
        // more columns here.
    }
    

    At the end, use the PredicateBuilder instance as arguments to your Where operator in the Linq query.

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