c# - 如何获得 2 个 IQueryable<T> 方法来实现析取?

标签 c# entity-framework iqueryable

如果这是一个简单的问题,我深表歉意;假设我有一个带有 2 个方法的 EF 查询对象:

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 的 2 种查询方法?我想要四足动物或没有尾部的动物

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

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

谢谢


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

最佳答案

当您已经调用 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);
}

这基本上采用多个谓词,并通过使用 bool OR 组合表达式体来组合它们。由于我们要组合可能具有不同表达式参数的不同表达式,因此我们还需要确保使用公共(public)参数替换表达式主体中的所有表达式参数引用。我们使用简单的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>>相反。

关于c# - 如何获得 2 个 IQueryable<T> 方法来实现析取?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35648669/

相关文章:

c# - 通过网络共享的 SQL CE

c# - 将 KeyDown 键转换为一个字符串 C#

c# - ASP.NET Core 2.2 在多对多插入时返回 "NotSupportedException: Collection was of a fixed size"

C# SMO - 将表数据脚本化到文件。 throw 错误

c# - 如何创建一个通用方法来遍历对象的字段并将其用作 Where 谓词?

c# - 当尝试使用Task.Run()在MySql中运行插入查询时,它抛出以下异常: "Unable to connect to any of the specified MySQL hosts."

c# - 我可以将 DbSet 和 BindingList 绑定(bind)在一起以在 ListBox 中显示数据库内容吗?

c# - 尝试使用 Expression<Func<..>> 从方法调用 Any() 时出现类型错误

linq - 如何使 EF-Core (2.1) 编译查询返回 IQueryable?

c# - 如何在 C# 中制作自定义控件?