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

时间:2015-10-12 13:21:47

标签: c# .net-4.0 expression-trees

我想要一个函数Expression> AnyColumnContains(string [] value) 迭代表的所有列并根据列检查值的数组,并且只有在任何列中包含每个值时才返回true。

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

这就是我所拥有的:

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);
}

1 个答案:

答案 0 :(得分:4)

您可以简单地编写已经拥有的方法,而不是从头开始创建一个全新的方法。

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

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));
}