2009-02-20 18 views
11

Tôi có một truy vấn nơi tôi đã sử dụng các câu lệnh WhereWhereBetween khác nhau để thu hẹp bộ sưu tập xuống một tập hợp nhất định. Bây giờ Tôi cần thêm loại Where || WhereBetween. Nói cách khác, tôi không thể kết nối chúng lại với nhau như tôi có cho đến bây giờ, vì điều đó sẽ hoạt động như một Và. Vì vậy, làm thế nào tôi có thể làm điều này?C#, Linq2Sql: Có thể kết nối hai truy vấn thành một không?

tôi thấy hai khả năng:

  1. Tạo hai queryables từ một trong tôi có, người ta sử dụng Where, và một sử dụng WhereBetween. Và sau đó ghép chúng lại. Không biết nếu điều này thậm chí có thể? Ngoài ra, mặc dù không phải trong trường hợp cụ thể của tôi, bạn rất có thể sẽ kết thúc bằng các bản sao ...
  2. Bằng cách nào đó kết hợp biểu thức Where và biểu thức được tạo trong WhereBetween với một số loại Hoặc.

Việc đầu tiên, như đã đề cập, tôi không chắc chắn là có thể. Và nếu nó là, tôi không chắc chắn đó là một cách tốt để làm điều đó.

Thứ hai, tôi có thể xem như một tùy chọn, nhưng không hoàn toàn chắc chắn về tất cả các chi tiết. Dưới đây là phương pháp WhereBetween từ câu hỏi khác của tôi, mà tôi hiện đang sử dụng và nó hoạt động tuyệt vời:

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)); 
    } 

Tôi nghĩ rằng tôi có thể có thể trích xuất các phần xây dựng biểu hiện của nó vào một phương pháp mới. Có lẽ như thế này:

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); 
    } 

Sau đó tôi có thể sử dụng phương pháp mới để nhận biểu thức thay vì truy vấn được. Vì vậy, hãy nói rằng tôi có số WhereBetween(ø => ø.Id, someRange) và ví dụ: ø => ø.SomeValue == null. Làm thế nào tôi có thể kết hợp cả hai với Or? Tôi đang xem số Expression.OrElse được sử dụng theo phương pháp WhereBetween và tôi nghĩ đó có thể là những gì tôi cần hoặc có thể là Expression.Or. Nhưng tôi rất không ổn định về công cụ biểu hiện này, vì vậy tôi không chắc chắn nên chọn gì ở đây, hoặc thậm chí nếu tôi đi đúng hướng: p

Ai đó có thể cho tôi một số gợi ý ở đây?

Trả lời

9

Bạn có hai tùy chọn ở đây - Queryable.Union hoặc kết hợp biểu thức. Tôi muốn nói chung ủng hộ sau này, qua OrElse - mà (với LINQ-to-SQL ít nhất), bạn có thể làm với 2 biểu thức (xem dưới đây) - nhưng trong cả hai trường hợp nó nên được bao gồm:

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

Cả arr1arr2 mỗi chỉ thực hiện 1 lần truy cập cơ sở dữ liệu (mặc dù TSQL khác nhau; đầu tiên có một số OR trong mệnh đề WHERE; thứ hai có hai truy vấn riêng biệt với UNION).

Dưới đây là các phương pháp khuyến nông tôi đã sử dụng:

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

Đó điều hàng có thể là bất cứ điều gì phải không?(Bất kỳ chuỗi đó là, tất nhiên) – Svish

+0

Suy nghĩ về hiệu suất sql và như vậy, là nó hiệu quả hơn với một 'UNION' hoặc một' OR' trong 'WHERE'clause? – Svish

+0

Rất đẹp. Câu hỏi này tiếp tục xuất hiện (trong các biến thể) và OrElse của bạn chỉ có thể là viên đạn ma thuật giải quyết tất cả. –

Các vấn đề liên quan