2009-02-20 7 views
11

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

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

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

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

ответ

9

У вас здесь есть два варианта - Queryable.Union или комбинация выражений. Я бы вообще в пользу последнего, через OrElse - который (с LINQ к 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); 
} 
+0

Эта строка вещь может быть что угодно, не так ли?(Любая строка, которая, конечно) – Svish

+0

Думая о производительности sql и т. Д., Эффективнее ли это с UNION или OR в 'WHERE'clause? – Svish

+0

Очень приятно. Этот вопрос продолжает расти (в вариациях), и ваш OrElse просто может быть волшебной пулей, которая решает их всех. –

Смежные вопросы