扩展IQueryable< T> Where()作为OR而不是AND关系 [英] Extend IQueryable<T> Where() as OR instead of AND relationship

查看:84
本文介绍了扩展IQueryable< T> Where()作为OR而不是AND关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using my own extension methods of IQueryable<> to create chainable queries such as FindAll().FindInZip(12345).NameStartsWith("XYZ").OrderByHowIWantIt() etc. which then on deferred execution creates a single query based on my extension methods chain.

尽管如此,但问题是,扩展链中的所有位置(FindXYZ,FindInZip等)将始终结合为AND,这意味着我无法执行以下操作:

The problem with this though, is that all Where's in the extension chain (FindXYZ, FindInZip etc.) will always combine as AND which means I can't do something like this:

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

FindAll().FirstNameStartsWith("X").OrLastNameStartsWith("Z") because I don't know how I can inject the OR in a separate Where method.

有什么办法可以解决这个问题吗?

Any idea how I can solve this?

其他; 到目前为止,我了解如何将表达式链接为或是否包装它们(例如CompileAsOr(FirstNameStartsWith("A").LastNameStartsWith("Z").OrderBy(..))

additional; So far I understand how to chain expressions as Or if I wrap them (e.g. CompileAsOr(FirstNameStartsWith("A").LastNameStartsWith("Z").OrderBy(..))

尽管我想要做的事情稍微复杂一点(PredicateBuilder在这里无济于事.),因为我希望以后的IQueryable基本上访问之前建立的Where条件,而不必包装它们来创建他们之间的或".

What I'm trying to do though is slightly more complicated (and PredicateBuilder doesn't help here..) in that I want a later IQueryable to basically access the Where conditions that were established prior without having to wrap them to create the Or between them.

随着每个扩展方法返回IQueryable<>,我了解它应该对某个地方的查询条件的当前状态有所了解,这使我相信应该有一些自动化的方法或在所有先前的Where条件中创建一个Or而不必须包装您想要的东西.

As each extension method returns IQueryable<> I understand that it should have the knowledge about the current status of query conditions somewhere, which leads me to believe that there should be some automated way or creating an Or across all prior Where conditions without having to wrap what you want Or'd.

推荐答案

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

I'm assuming the different parts of the query are only known at runtime, i.e. you can't just use || in a where...

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

One lazy option is Concat - but this tends to lead to poor TSQL etc; however, I tend to be inclined to write custom Expressions instead. The approach to take depends on what the provider is, as LINQ-to-SQL supports different options to EF (for example) - which has a genuine impact here (since you can't use sub-expressions with EF). Can you tell us which?

以下是一些应与LINQ-to-SQL一起使用的代码;如果您构建表达式的数组(或列表,并调用.ToArray()),它应该可以正常工作;例子是LINQ-to-Objects,但仍然可以使用:

Here's some code that should work with LINQ-to-SQL; if you build an array (or list, and call .ToArray()) of expressions, it should work fine; example is LINQ-to-Objects, but should still work:

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

这篇关于扩展IQueryable&lt; T&gt; Where()作为OR而不是AND关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆