2013-08-23 66 views
4

我有一個方法需要一個IOrderedQueryable和表達式< Func < T,V >>用作SQL數據庫中的過濾器和頁面記錄。轉換表達式<Func <T,可爲空<V> >>

var query = contexBills.AsNoTracking().Where(x => x.Complete==true).OrderBy(x => x.BillID); 

var reader = new BulkReader<Bill>(query, x => x.BillId, 10000); 

大部分讀者在整個代碼廣泛用於頁面大量的記錄,並分批處理它們,並這樣定義

public BulkReader(IOrderedQueryable<T> queryable, Expression<Func<T, Object>> selector, int blockSize = 1000) 

對於優化尋呼開始在找到的最低值表格並以最大值結束。由於使用Skip()函數的數據庫每月有數百萬條記錄,當你到達表中數百萬的數據時,Take()方法會降低到每頁大約13秒,然後處理整個月份的數據可能會花費很多時間小時。

鑑於集合中只有極少數記錄被標記爲完成== false,因此只需選擇記錄> [頁面開始]和每分鐘記錄約100萬條記錄。在某些情況下,您處理的內容稍稍小於傳入的blockSize,但min和max之間的所有記錄均得到處理。

隨着月數的增加,最小值增加,所以假設0爲最小值會浪費許多根本不返回的SQL調用。

所以我必須讓這些價值觀是

var min = queryable.Select(selector).DefaultIfEmpty(0).Min(); 
var max = queryable.Select(selector).DefaultIfEmpty(0).Max(); 

將會產生SQL,看起來像這樣

SELECT 
[GroupBy1].[A1] AS [C1] 
FROM (SELECT 
MIN([Join1].[A1]) AS [A1] 
FROM (SELECT 
    CASE WHEN ([Project1].[C1] IS NULL) THEN 0 ELSE [Project1].[PrintSummaryID] END AS [A1] 
    FROM (SELECT 1 AS X) AS [SingleRowTable1] 
    LEFT OUTER JOIN (SELECT 
     [Extent1].[PrintSummaryID] AS [PrintSummaryID], 
     cast(1 as tinyint) AS [C1] 
     FROM [dbo].[tblPrintSummary] AS [Extent1]) AS [Project1] ON 1 = 1 
) AS [Join1] 
) AS [GroupBy1] 
GO 

如果我把代碼(作爲測試),以使這樣的

電話
var min = queryable.Min(x =>(int?)x.BillID) ?? 0; 
var max = queryable.Max(x =>(int?)x.BillID) ?? 0; 

那麼產生的SQL是

SELECT 
[GroupBy1].[A1] AS [C1] 
FROM (SELECT 
MIN([Extent1].[PrintSummaryID]) AS [A1] 
FROM [dbo].[tblPrintSummary] AS [Extent1] 
) AS [GroupBy1] 
GO 

同樣可以通過聲明以下,而不是實現:

Expression<Func<Bill, int?>> selector2 = x => x.BillID; 

這給更簡單更快SQL執行的好處,並允許代碼變成:

var min = queryable.Select(selector2).Min() ?? 0; 
var max = queryable.Select(selector2).Max() ?? 0; 

服用明確重新定義所有選擇器併爲其提供覆蓋的方法將意味着在整個應用程序中進行重要的重複和重新編碼

我怎樣才能拿到一個原始的選擇器,並轉換爲可空的版本等效的一般,而不必明確地編碼每一個。

var selector2 = selector.NullableExpression(); 

我想此作爲表達< Func鍵< T時的擴展方法NullableExpression(),V >>使得我返回ExpressionExpression < Func鍵< T,可空< V >>>和我可以在我的代碼中的其他位置使用它。

我努力如何將V轉換爲Nullable或V?在表達式中。

回答

5

非常簡單,真的。訣竅是使用源表達式的主體,同時重用其參數。

public static Expression<Func<T, V?>> ToNullableExpression<T, V> 
    (this Expression<Func<T, V>> source) where V : struct 
{ 
    if(source == null) 
     throw new ArgumentNullException("source"); 

    var body = Expression.Convert(source.Body, typeof(V?)); 
    var parameters = source.Parameters; 

    return Expression.Lambda<Func<T, V?>>(body, parameters); 
} 
+0

工作就像一個魅力...謝謝這麼多... –

+0

我會用自己ExpressionVisitor,因爲在某些情況下,你可能會發生問題的EntityFramework,如果源表達不完全是短暫的。也就是說,如果結果和輸入都綁定到EntityFramework上,則會拋出異常。 – Aron

+0

你也可以使用David Fowl的Query Interceptor來動態地轉換'.Min(謂詞)?? 0'到'.Min(predicate.ToNullableExpression())'在Execute()。 – Aron

相關問題