2009-02-16 29 views
8

Cho phép nói rằng tôi có một thứ gọi là Stuff trong cơ sở dữ liệu của tôi, với thuộc tính có tên là Id. Từ người dùng, tôi nhận được một chuỗi các đối tượng Range đã chọn (hoặc đúng hơn là tôi tạo chúng từ đầu vào của chúng) với các Id mà chúng muốn. Một phiên bản rút gọn của struct trông như thế này:C#, Linq2SQL: Tạo một vị từ để tìm các phần tử trong một số phạm vi

public struct Range<T> : IEquatable<Range<T>>, IEqualityComparer<Range<T>> 
{ 
    public T A; 
    public T B; 
    public Range(T a, T b) 
    { 
     A = a; 
     B = b; 
    } 
    ... 
} 

Vì vậy, người ta có thể ví dụ đã nhận:

var selectedRange = new List<Range<int>> 
    { 
     new Range(1, 4), 
     new Range(7,11), 
    }; 

sau đó tôi muốn sử dụng để tạo ra một vị từ để chỉ chọn những thứ trong đó có một giá trị giữa chúng. Ví dụ, bằng cách sử dụng PredicateBuilder, tôi có thể ví dụ như làm điều đó theo cách này:

var predicate = PredicateBuilder.False<Stuff>(); 
foreach (Range<int> r in selectedRange) 
{ 
    int a = r.A; 
    int b = r.B; 
    predicate = predicate.Or(ø => ø.Id >= a && ø.Id <= b); 
} 

và sau đó:

var stuff = datacontext.Stuffs.Where(predicate).ToList(); 

Những công trình! Những gì tôi muốn làm bây giờ, là tạo ra một phương pháp mở rộng chung để tạo ra những vị từ cho tôi. Loại như thế này:

public static Expression<Func<T,bool>> ToPredicate<T>(this IEnumerable<Range<int>> range, Func<T, int> selector) 
{ 
    Expression<Func<T, bool>> p = PredicateBuilder.False<T>(); 
    foreach (Range<int> r in range) 
    { 
     int a = r.A; 
     int b = r.B; 
     p = p.Or(ø => selector(ø) >= a && selector(ø) <= b); 
    } 
    return p; 
} 

Vấn đề ở đây, là nó bị treo với một NotSupportedException do bộ chọn (ø) gọi: Method 'System.Object DynamicInvoke(System.Object[])' has no supported translation to SQL.

Tôi đoán đó là điều dễ hiểu. Nhưng có cách nào để giải quyết vấn đề này không? Những gì tôi muốn kết thúc với là vì vậy mà tôi chỉ có thể làm:

var stuff = datacontext.Stuffs.Where(selectedRange.ToPredicate<Stuff>(ø => ø.Id)); 

Hoặc thậm chí tốt hơn, tạo ra cái gì đó trả về một IQueryable vì vậy mà tôi chỉ có thể làm:

var stuff = datacontext.Stuffs.WhereWithin<Stuff>(selectedRange, ø => ø.Id); // Possibly without having to specify Stuff as type there... 

Vì vậy, bất kỳ ý tưởng? Tôi thực sự muốn làm việc này, gây ra nếu không tôi sẽ nhận được rất nhiều những khối foreach mã, tạo vị ...


Lưu ý 1: Tất nhiên, sẽ thật tuyệt nếu tôi có thể mở rộng đến nhiều hơn int, như DateTime và như vậy, nhưng không chắc chắn làm thế nào mà kết thúc bằng cách sử dụng các toán tử> = và < = ... CompareTo có hoạt động với linq-to-sql không? Nếu không có vấn đề gì khi tạo hai. Một cho int và một cho DateTime, vì đó là chủ yếu là các loại này sẽ được sử dụng cho.

Lưu ý 2: Nó sẽ được sử dụng để báo cáo, nơi người dùng sẽ có thể thu hẹp những gì xuất hiện, dựa trên những thứ khác nhau. Giống như, tôi muốn báo cáo này cho những người đó và những ngày đó.

Trả lời

7

Việc sử dụng với generics là vấn đề, vì C# không hỗ trợ các toán tử trên generics - nghĩa là bạn phải viết biểu thức theo cách thủ công. Và như chúng ta đã thấy, chuỗi hoạt động khác nhau. Nhưng đối với phần còn lại, làm thế nào về một cái gì đó tương tự (chưa được kiểm tra):

(sửa cho nhiều dãy)

public static IQueryable<TSource> WhereBetween<TSource, TValue>(
     this IQueryable<TSource> source, 
     Expression<Func<TSource, TValue>> selector, 
     params Range<TValue>[] ranges) 
    { 
     return WhereBetween<TSource,TValue>(source, selector, 
      (IEnumerable<Range<TValue>>) ranges); 
    } 

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

Lưu ý; việc sử dụng Expression.Invoke có nghĩa là nó có thể hoạt động trên LINQ-to-SQL nhưng không phải EF (hiện tại, hy vọng được sửa trong 4.0).

Với việc sử dụng (thử nghiệm trên Northwind):

Range<decimal?> range1 = new Range<decimal?>(0,10), 
       range2 = new Range<decimal?>(15,20); 
var qry = ctx.Orders.WhereBetween(order => order.Freight, range1, range2); 

Tạo TSQL (tái định dạng):

SELECT -- (SNIP) 
FROM [dbo].[Orders] AS [t0] 
WHERE (([t0].[Freight] >= @p0) AND ([t0].[Freight] <= @p1)) 
OR (([t0].[Freight] >= @p2) AND ([t0].[Freight] <= @p3)) 

Chỉ cần những gì chúng tôi muốn ;-p

+0

Làm cách nào có thể hoạt động với toàn bộ chuỗi đối tượng Phạm vi ? – Svish

+0

Bạn có thể làm tương tự với OrElse ... Tôi sẽ cập nhật ... –

+0

Và "x" này trong thông số của bạn là gì? – Svish

0

Bạn đang nhận lỗi , bởi vì mọi thứ cho LINQ to SQL cần phải ở dạng Expression. Hãy thử điều này

public static Expression<Func<T,bool>> ToPredicate<T>(
    this IEnumerable<Range<int>> range, 
    Expression<Func<T, int>> selector 
) { 
    Expression<Func<T, bool>> p = PredicateBuilder.False<T>(); 
    Func<T, int> selectorFunc = selector.Compile(); 
    foreach (Range<int> r in range) 
    { 
     int a = r.A; 
     int b = r.B; 
     p = p.Or(ø => selectorFunc(ø) >= a && selectorFunc(ø) <= b); 
    } 
    return p; 
} 

Lưu ý rằng tôi biên dịch bộ chọn trước khi sử dụng. Điều này sẽ làm việc với ra một xô, tôi đã sử dụng một cái gì đó giống như nó trong quá khứ.

+0

Cách khác, tôi phải thay thế 'selector (ø)' bằng 'p = p.Or (ø => selector (ø)> = a && selector (ø) <= b); '? – Svish

+0

Không, tôi nhận được cùng một' Phương thức 'System.Object DynamicInvoke (System.Object [])' không có bản dịch được hỗ trợ cho SQL.' khi làm điều đó = / – Svish

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