Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 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 каждый из них только 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);
}