将IQueryable <T> Where()扩展为OR而不是AND关系

Ale*_*lex 8 c# iqueryable

我正在使用我自己的IQueryable <>扩展方法来创建可链接查询,例如FindAll().FindInZip(12345).NameStartsWith("XYZ").OrderByHowIWantIt()等然后在延迟执行时创建基于我的单个查询扩展方法链.

但问题是,扩展链中的所有位置(FindXYZ,FindInZip等)将始终组合为AND,这意味着我无法做到这样的事情:

FindAll().FirstNameStartsWith("X").OrLastNameStartsWith("Z")因为我不知道如何在一个单独的Where方法中注入OR.

知道如何解决这个问题吗?


额外; 到目前为止,我理解如何将表达式链接为或者如果我将它们包装起来(例如CompileAsOr(FirstNameStartsWith("A").LastNameStartsWith("Z").OrderBy(..))

我想要做的虽然稍微复杂一点(并且PredicateBuilder在这里没有帮助......)因为我希望以后的IQueryable能够基本上访问先前建立的Where条件,而不必将它们包装起来以创建Or之间他们.

当每个扩展方法返回IQueryable <>时,我理解它应该知道某个地方的查询条件的当前状态,这使我相信应该有一些自动化的方法或在所有先前的Where条件中创建Or而不必包装你想要什么或者.

Mar*_*ell 11

我假设查询的不同部分仅在运行时已知,即您不能只||where...中使用...

一个懒惰的选择是Concat- 但这往往导致差的TSQL等; 但是,我倾向于编写自定义Expressions.采用的方法取决于提供者是什么,因为LINQ-to-SQL支持EF的不同选项(例如) - 这在此具有真正的影响(因为你不能在EF中使用子表达式).你能告诉我们哪个?


这里有一些应该与LINQ-to-SQL一起使用的代码; 如果你构建一个.ToArray()表达式的数组(或列表,并调用),它应该工作正常; 示例是LINQ到对象,但应该仍然有效:

    static void Main()
    {
        var data = (new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).AsQueryable();

        var predicates = new List<Expression<Func<int, bool>>>();
        predicates.Add(i => i % 3 == 0);
        predicates.Add(i => i >= 8);           

        foreach (var item in data.WhereAny(predicates.ToArray()))
        {
            Console.WriteLine(item);
        }
    }

    public static IQueryable<T> WhereAny<T>(
        this IQueryable<T> source,
        params Expression<Func<T,bool>>[] predicates)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (predicates == null) throw new ArgumentNullException("predicates");
        if (predicates.Length == 0) return source.Where(x => false); // no matches!
        if (predicates.Length == 1) return source.Where(predicates[0]); // simple

        var param = Expression.Parameter(typeof(T), "x");
        Expression body = Expression.Invoke(predicates[0], param);
        for (int i = 1; i < predicates.Length; i++)
        {
            body = Expression.OrElse(body, Expression.Invoke(predicates[i], param));
        }
        var lambda = Expression.Lambda<Func<T, bool>>(body, param);
        return source.Where(lambda);
    }
Run Code Online (Sandbox Code Playgroud)

  • 从历史中提出这一点,因为它仍然适用于 EF Core。 (2认同)

Meh*_*ari 8

使用PredicateBuilder<T>.这可能是你想要的.