C#,Linq2Sql:是否可以将两个可查询对象连接成一个?

11

我有一个可查询对象,在其中使用了各种WhereWhereBetween语句来将集合缩小到一定范围。现在我需要添加一种Where || WhereBetween。换句话说,我不能像以前那样将它们链接在一起,因为这将作为一个And运算。那么,我该怎么做?

我看到两种可能性:

  1. 从现有的Queryable创建两个Queryable,一个使用Where,另一个使用WhereBetween然后将它们连接起来。不知道是否可能?而且,虽然不是在我特定的情况下,你很可能最终会得到重复的数据...
  2. 以某种方式将Where表达式和在WhereBetween中创建的表达式合并,使用Or等逻辑符号。

首先提到的第一种可能性,我不确定是否可能。即使可能,我也不确定这是否是一个好方法。

第二种可能性,我认为是一种可选方案,但并不完全确定所有细节。下面是我另一个问题中的WhereBetween方法,我现在使用它,它工作得很好:

    public static IQueryable<TSource> WhereBetween<TSource, TValue>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, TValue>> selector,
        IEnumerable<Range<TValue>> ranges)
    {
        var param = Expression.Parameter(typeof(TSource), "x");
        var member = Expression.Invoke(selector, param);
        Expression body = null;
        foreach (var range in ranges)
        {
            var filter = Expression.AndAlso(
                Expression.GreaterThanOrEqual(member,
                     Expression.Constant(range.A, typeof(TValue))),
                Expression.LessThanOrEqual(member,
                     Expression.Constant(range.B, typeof(TValue))));
            body = body == null ? filter : Expression.OrElse(body, filter);
        }
        return body == null ? source : source.Where(
            Expression.Lambda<Func<TSource, bool>>(body, param));
    }

我在思考,也许可以将其表达式构建部分提取出来放到一个新的方法中,类似于这样:

    public static IQueryable<TSource> WhereBetween<TSource, TValue>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, TValue>> selector,
        IEnumerable<Range<TValue>> ranges)
    {
        return source.Where(WhereBetween(selector, ranges));
    }

    public static Expression<Func<TSource, bool>> WhereBetween<TSource, TValue>(
        Expression<Func<TSource, TValue>> selector,
        IEnumerable<Range<TValue>> ranges)
    {
        var param = Expression.Parameter(typeof(TSource), "x");
        var member = Expression.Invoke(selector, param);
        Expression body = null;
        foreach (var range in ranges)
        {
            var filter = Expression.AndAlso(
                Expression.GreaterThanOrEqual(member,
                     Expression.Constant(range.A, typeof(TValue))),
                Expression.LessThanOrEqual(member,
                     Expression.Constant(range.B, typeof(TValue))));
            body = body == null ? filter : Expression.OrElse(body, filter);
        }
        return body == null 
            ? ø => true
            : Expression.Lambda<Func<TSource, bool>>(body, param);
    }
我可以使用新方法来获取表达式,而不是查询。假设我有WhereBetween(ø => ø.Id, someRange)ø => ø.SomeValue == null,我应该如何将它们与Or组合起来呢?我查看了在WhereBetween方法中使用的Expression.OrElse,我认为这可能就是我需要的,或者也许是Expression.Or。但是,我在处理表达式方面很不稳定,所以我不确定该选择什么,甚至不确定自己是否正确 :p 请问有人能给我一些指导吗?
1个回答

9

这里有两个选项 - Queryable.Union,或表达式组合。我通常更喜欢后者,通过OrElse - 至少在使用LINQ-to-SQL时可以使用2个表达式(见下文) - 但无论哪种情况都应该被组合:

    using(var ctx = new DataClasses1DataContext())
    {
        ctx.Log = Console.Out;
        Expression<Func<Customer, bool>> lhs =
            x => x.Country == "UK";
        Expression<Func<Customer, bool>> rhs =
            x => x.ContactName.StartsWith("A");

        var arr1 = ctx.Customers.Where(
            lhs.OrElse(rhs)).ToArray();

        var arr2 = ctx.Customers.Where(lhs)
            .Union(ctx.Customers.Where(rhs)).ToArray();
    }

arr1arr2都只执行了1次数据库查询(尽管TSQL不同;第一个在WHERE子句中有一个OR,而第二个具有两个带有UNION的单独查询)。

这是我使用的扩展方法:

static Expression<Func<T, bool>> OrElse<T>(
    this Expression<Func<T, bool>> lhs,
    Expression<Func<T, bool>> rhs)
{
    var row = Expression.Parameter(typeof(T), "row");
    var body = Expression.OrElse(
        Expression.Invoke(lhs, row),
        Expression.Invoke(rhs, row));
    return Expression.Lambda<Func<T, bool>>(body, row);
}

那个行的东西可以是任何东西,对吧?(当然是任何字符串) - Svish
考虑到 SQL 性能等因素,使用 UNION 还是在 WHERE 子句中使用 OR 更有效率? - Svish
非常好。这个问题一直在不断出现(以各种变体形式),而你的 OrElse 可能就是解决所有问题的灵丹妙药。 - Amy B
是的,它可以是“row”,“x”或“Fred”。关于性能:您需要对其进行分析...不同的查询可能会有不同的行为。 一般来说,OR会更有效率,但也有一些情况下UNION会更好;还有一些情况下两个单独的查询会更好。 - Marc Gravell
不错。表达式的东西慢慢地开始有点意义了,呵呵。看起来它会工作,尽管我要等到周一才能完全测试它。可能会坚持使用OR,因为在一般情况下更高效 =) 再次感谢你! - Svish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接