2012-10-29 122 views
3

我在寫一個LINQ 2 SQL擴展方法的集合。現在,我有一堆重複的方法,一個在IEnumerable集合上工作,另一個在IQueryable集合上工作。例如C#:使用不同的類型參數重載LINQ泛型方法

public static IQueryable<X> LimitVisible(this IQueryable<X> items) { 
    return items.Where(x => !x.Hidden && !x.Parent.Hidden); 
} 

public static IEnumerable<X> LimitVisible(this IEnumerable<X> items) { 
    return items.Where(x => !x.Hidden && !x.Parent.Hidden); 
} 

我想我需要這樣的重複,因爲我希望能夠保留在某些情況下IQueryable的行爲,讓執行被推遲到數據庫中。 這是一個正確的假設,還是單個IEnumerable方法(加上將輸出轉換爲IQueryable)的工作?如果我需要兩個,我不喜歡重複,所以我想用一般的方法

public static T LimitVisible<T>(this T items) where T : IEnumerable<X> { 
    return (T)items.Where(x => !x.Hidden && !x.Parent.Hidden); 
} 

這工作,除了我還有其他類型的比X將兩者結合起來(說Y和Z),其我也想寫爲LimitVisible函數。但我不能寫

public static T LimitVisible<T>(this T items) where T : IEnumerable<Y> { 
    return (T)items.Where(y => !y.Hidden && !y.Parent.Hidden); 
} 

因爲您可以基於泛型進行重載。我可以將這些方法放在不同的類中,但這似乎不是正確的解決方案。

有什麼建議嗎?也許我做了不正確的假設,並且首先不需要特定的IQueryable版本。

編輯:另一種選擇

下面是我在過去使用的另一種模式,以避免重複

private static readonly Expression<Func<X, bool>> F = x => !x.Hidden && !x.Parent.Hidden; 

public static IEnumerable<X> LimitVisible(this IEnumerable<X> e) { 
    return e.Select(W.Compile()); 
} 

public static IQueryable<X> LimitVisible(this IQueryable<X> q) { 
    return q.Select(W); 
} 

是否有任何與此危險?

+0

不X,Y,Z有一個共同的接口或基類的'Hidden'和'父'屬性? – Magnus

+0

不,它們是數據庫中不同的表格,它們都碰巧具有隱藏字段。 –

回答

1

答案取決於您的使用情況:如果您不介意將數據集帶入內存以執行查詢,那麼只保留IEnumerable<T>覆蓋即可。

但是,這個決定的不可避免的結果是,在您使用LimitVisible<T>時,您的查詢將被強制記憶。這可能是也可能不是你想要的,並可能迫使你編碼模式,你寧願避免。例如,

var firstHundred = ctx 
    .SalesOrders 
    .LimitVisible()      // Happens in memory 
    .Where(ord => ord.UserId == myUser) // Also happens in memory 
    .Take(100); 

將確定自己的知名度,而不是檢查之前執行差了很多與單個IEnumerable<T>比使用IQueryable<T>超越控制availalbe,因爲所有銷售訂單將被檢索到內存中一個接一個在服務器端的條件。如果用戶ID過濾可以消除在服務器端的大部分銷售訂單,這相當於查詢的性能差異可能是數量級更好的訂單:

​​

如果您計劃只有你自己,你用你的代碼不介意在沒有明顯原因的情況下表現不佳的情況下注意情況,您可以保持一次過載。如果你打算讓別人使用你的圖書館,我強烈建議保持兩個覆蓋。

編輯:爲了加快您的另一種實現,預編譯你的表情,就像這樣:

private static readonly Expression<Func<X, bool>> F = x => !x.Hidden && !x.Parent.Hidden; 
private static readonly Predicate<X> CompiledF = (Predicate<X>)F.Compile(); 

public static IEnumerable<X> LimitVisible(this IEnumerable<X> e) { 
    return e.Select(CompiledF); 
} 

public static IQueryable<X> LimitVisible(this IQueryable<X> q) { 
    return q.Select(F); 
} 
+0

您證實了我的懷疑。我絕對不想把一切都帶入記憶。有兩種方法的問題是,很容易意外更新代碼而不是兩者。 –

+0

@JordanCrittenden不幸的是,這可能是隨走,無需編寫大量複雜的代碼重用裏面IQueryable的''實現LINQ的表情最簡單的方法。 – dasblinkenlight

+0

是的,我剛剛編輯,以顯示替代。我認爲這種方法效率很低,但由於編譯調用 –

2

真的沒有兩種方法的好方法。 IQueryableWhereExpression<Func<T,bool>>作爲參數,而IEnumerableFunc<T,bool>作爲參數。由於lambda表達式的魅力,調用者的代碼看起來完全相同,但編譯器實際上對兩個不同的代碼位做了大不相同的事情。

+0

對不起,這是我剛剛修復的一個錯字。 –

+0

@JordanCrittenden刪除了我答案的那一部分,但其餘部分仍然相關。 – Servy