11

我有一个可查询的地方,我使用了各种WhereWhereBetween语句将集合缩小到某个集合。现在我需要添加一种Where || WhereBetween. 换句话说,我不能像到目前为止那样将它们链接在一起,因为这将作为 And 起作用。那么,我该怎么做呢?

我看到两种可能性:

  1. 从我拥有的一个中创建两个可查询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)and 例如ø => ø.SomeValue == null。我怎样才能将这两者与 Or 结合起来?我正在查看方法中Expression.OrElse使用的WhereBetween方法,我认为这可能是我需要的,或者这可能是Expression.Or. 但是我对这个表达的东西很不稳定,所以我不确定在这里选择什么,或者即使我在正确的轨道上:p

有人可以在这里给我一些指示吗?

4

1 回答 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();
    }

两者都只arr1执行arr2一次数据库命中(尽管 TSQL 不同;第一个ORWHERE子句中有一个;第二个有两个单独的查询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);
}
于 2009-02-20T14:25:42.097 回答