C #, Linq2Sql: можно ли объединить два объекта запроса в один?

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

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

  1. Создайте два запроса из того, который у меня есть, один с помощью Where, а другой с помощью WhereBetween. А затем соедините их. Не знаю, возможно ли это вообще? Кроме того, хотя и не в моем конкретном случае, вы, скорее всего, получите дубликаты ...
  2. Каким-то образом объедините выражение Where и выражение, созданное в WhereBetween, с каким-то ИЛИ.

Первый, как уже упоминалось, я не уверен, что это вообще возможно. А если бы это было так, я не уверен, что это хороший способ сделать это.

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

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


person Svish    schedule 20.02.2009    source источник


Ответы (1)


Здесь у вас есть два варианта - Queryable.Union или комбинация выражений. Я обычно предпочитаю последнее, через OrElse, которое (по крайней мере, с LINQ-to-SQL) вы можете сделать с помощью двух выражений (см. Ниже), но в любом случае оно должно быть составлено:

    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 отличается; первый имеет 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);
}
person Marc Gravell    schedule 20.02.2009
comment
Эта строчка может быть чем угодно, верно? (Конечно, любая строка) - person Svish; 20.02.2009
comment
Думая о производительности sql и тому подобном, что более эффективно с UNION или OR в WHEREclause? - person Svish; 20.02.2009
comment
Очень хорошо. Этот вопрос продолжает подниматься (в различных вариациях), и ваш OrElse может стать волшебной пулей, которая решит их все. - person Amy B; 20.02.2009
comment
Да, это может быть строка, x или Фред. Что касается производительности: вам нужно профилировать его ... разные запросы ведут себя по-разному. В общем OR было бы более эффективным, но есть случаи, когда UNION оказывается лучше; и случаи, когда лучше два отдельных запроса. - person Marc Gravell; 20.02.2009
comment
Хороший. Выражения медленно, но неуклонно обретают смысл, хе-хе. Похоже, он будет работать, хотя я не смогу полностью протестировать его до понедельника. Вероятно, будет придерживаться OR, так как это более эффективно в общем =) Еще раз спасибо! - person Svish; 20.02.2009