如何获得2个IQueryable <t>方法来实现析取?

时间:2016-02-26 10:02:44

标签: c# entity-framework iqueryable

如果这是一个简单的问题,请注意;假设我有一个带有两种方法的EF Query对象:

public static IQueryable<Animal> FourLegged(this IQueryable<Animal> query)
{
    return query.Where(r => r.NumberOfLegs == 4);
}

public static IQueryable<Animal> WithoutTail(this IQueryable<Animal> query)
{
    return query.Where(r => !r.HasTail);
}

现在,在我的服务层,为了获得四条腿而没有尾巴的动物,我可以这样做:

_animalService.Query()
.FourLegged()
.WithoutTail();

这将导致像这样的SQL查询:

select * from Animal where NumberOfLegs = 4 AND HasTail = 0

如何使用OR查询方法?我想要4腿或没有尾巴的动物

select * from Animal where NumberOfLegs = 4 OR HasTail = 0

在Nhibernate中我会使用一个简单的析取,但我在EF中找不到。

由于

解决方案:我最终使用了LinqKit predicates mentioned on this answer。它工作得很好,我也可以重用谓词。

1 个答案:

答案 0 :(得分:2)

当您调用query.Where()时,您无法真正执行此操作。已在IQueryable中收集了谓词,并且它们都由AND合并。

要获得OR,您必须进行一次query.Where()调用并传递一个涵盖各种析取谓词的表达式。

在您的情况下,组合谓词将如下所示:

query.Where(r => (r.NumberOfLegs == 4) || (!r.HasTail))

为了使其更具动态性,您基本上需要构建一个如下所示的自定义表达式组合函数:

Expression<Func<Animal, bool>> fourLegged = r => r.NumberOfLegs == 4;
Expression<Func<Animal, bool>> withoutTail = r => !r.HasTail;

query = query.Where(CombineDisjunctivePredicates(fourLegged, withoutTail));

让我们写下CombineDisjunctivePredicates函数:

public Expression<Func<T, bool>> CombineDisjunctivePredicates<T>(params Expression<Func<T, bool>>[] predicates)
{
    Expression current = Expression.Constant(false);
    ParameterExpression param = Expression.Parameter(typeof(T), "obj");

    foreach (var predicate in predicates)
    {
        var visitor = new ReplaceExpressionVisitor(predicate.Parameters[0], param);
        current = Expression.Or(current, visitor.Visit(predicate.Body));
    }

    return Expression.Lambda<Func<T, bool>>(current, param);
}

这基本上需要许多谓词,并通过使用布尔OR组合表达式主体来组合它们。由于我们正在组合可能具有不同表达式参数的不同表达式,因此我们还需要确保使用公共参数替换表达式主体中的所有表达式参数引用。我们使用简单的ReplaceExpressionVisitor执行此操作,可以像这样轻松实现:

public class ReplaceExpressionVisitor : ExpressionVisitor
{
    private readonly Expression _original;
    private readonly Expression _replacement;

    public ReplaceExpressionVisitor(Expression original, Expression replacement)
    {
        _original = original;
        _replacement = replacement;
    }

    public override Expression Visit(Expression node)
    {
        return node == _original ? _replacement : base.Visit(node);
    }
}

这就是组合谓词所需的全部内容。您只需确保立即更改方法,这样他们就不会自己调用query.Where,而是返回Expression<Func<Animal, bool>>