表达式树:遍历字符串并检查它们是否包含在另一个表达式中

Expression Tree: iterate through strings and check if they contained in another Expression

我想要一个函数 Expression> AnyColumnContains(string[] value) 遍历 table 的所有列并根据列检查值数组,并且 returns 仅当每个值都包含在任何列中时才为真。

我已经有一个函数可以将每一列与一个值相匹配,但我在扩展它以根据每个值检查列时遇到问题

这是我得到的:

Expression<Func<T, bool>> AnyColumnContains<T>(string value){
    var p = Expression.Parameter(typeof(T), "entity");

    var fieldAccessors = typeof(T)
        .GetProperties(BindingFlags.Instance | BindingFlags.Public)
        .Where(f => f.PropertyType == typeof(string))
        .Select(f => Expression.Property(p, f))
        .ToArray();

    var fieldArray = Expression.NewArrayInit(typeof(string), fieldAccessors);

    var concatCall = Expression.Call(typeof(string).GetMethod(
         "Concat", new[] { typeof(string[]) }), fieldArray);

    var contains = Expression.Call(
        concatCall,
        typeof(string).GetMethod("Contains", new[] { typeof(string) }),
        Expression.Constant(value));

    return Expression.Lambda<Func<T, bool>>(contains, p);
}

我尝试使用自己的扩展方法并将 Contains 替换为它,但问题是我使用 sqlite 并且无法转换表达式,因为提供者不知道这些方法

这就是我想要的:

Expression<Func<T, bool>> AnyColumnContains<T>(string[] values){
    // ... //

    var contains  = // build Expression Tree that matches all values against concatCall and only returns true if all values are contained.

    return Expression.Lambda<Func<T, bool>>(contains, p);
}

与其从头开始创建一个全新的方法,您可以简单地组合您已有的有效方法。

我们可以使用以下方法将谓词组合在一起:

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 secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
        return Expression.Lambda<Func<T, bool>>
              (Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
    }

    public static Expression<Func<T, bool>> And<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
        return Expression.Lambda<Func<T, bool>>
              (Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
    }
}

它依赖于以下方法将一个表达式的所有实例替换为另一个:

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

现在我们所要做的就是为每个值调用 AnyColumnContains 的单值版本,并 Or 所有结果一起调用:

public static Expression<Func<T, bool>> AnyColumnContains<T>(IEnumerable<string> values)
{
    return values.Select(value => AnyColumnContains<T>(value))
        .Aggregate((a, b) => a.Or(b));
}