C #, Linq2Sql: возможно ли объединить два запроса в один? - PullRequest
11 голосов
/ 20 февраля 2009

У меня есть один запрашиваемый , где я использовал различные операторы Where и WhereBetween, чтобы сузить коллекцию до определенного набора. Теперь Мне нужно добавить вид Where || WhereBetween. Другими словами, я не могу просто связать их вместе, как до сих пор, потому что это будет работать как А. Итак, как я могу это сделать?

Я вижу две возможности:

  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) и, например, ø => ø.SomeValue == null. Как я могу объединить эти два с Or? Я смотрю на Expression.OrElse, используемый в методе WhereBetween, и думаю, что это может быть то, что мне нужно, или, может быть, это Expression.Or. Но я очень нестабилен в этом выражении, поэтому я не уверен, что выбрать здесь, или даже если я на правильном пути: p

Может ли кто-нибудь дать мне несколько советов здесь?

1 Ответ

9 голосов
/ 20 февраля 2009

У вас есть два варианта здесь - 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 каждый выполняет только 1 попадание в базу данных (хотя TSQL различен; у первого есть OR в предложении WHERE; у второго два отдельных запроса с 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);
}
...