2016-09-27 55 views
1

我需要的是代表通過Linq.Expressions此查詢:的GroupBy查詢通過Linq.Expressions和Lambda表達式

db.Documents.GroupBy(a => 1).Select(b => b.Sum(c => c.Amount) }); 

這是我到目前爲止有:

IQueryable<Document> data = db.Documents; 

ParameterExpression pe = Expression.Parameter(typeof(Document), "doc"); 

Expression groupBy = Expression.Call(
    typeof(Queryable), 
    "GroupBy", 
    new Type[] { typeof(Document), typeof(int) }, 
    data.Expression, 
    Expression.Lambda(Expression.Constant(1), pe)); 

ParameterExpression peg = Expression.Parameter(typeof(IGrouping<int, Document>), "group"); 

Expression select = Expression.Call(
    typeof(Queryable), 
    "Select", 
    new Type[] { typeof(IGrouping<int, Document>), typeof(int) }, 
    groupBy, 
    Expression.Lambda(Expression.Property(peg, "Key"), peg)); 

foreach (var item in data.Provider.CreateQuery(select)) { ... } 

這是實施:

db.Documents.GroupBy(a => 1).Select(b => b.Key }); 

它完美的工作。現在,我想彙總一筆,而不是訪問組的關鍵。

這是對我來說很棘手的地方。我想是這樣的:在

...b.Sum(c => c.Amount) 

ParameterExpression pe1 = Expression.Parameter(typeof(Document), "other"); 

Expression sum = Expression.Call(
    typeof(Queryable), 
    "Sum", 
    new Type[] { typeof(Document) }, 
    peg, 
    Expression.Lambda(Expression.Property(pe1, "Amount"), pe1)); 

而且,Sum函數智能感知給人簽名:

IEnumerable<Document>.Sum<Document>(Func<Document, decimal> selector) 

雖然:

db.Documents.Sum(a => a.Amount) 

我得到:

IQueryable<Document>.Sum<Document>(Expression<Func<Document, decimal>> selector) 

選擇器是Func在一個版本和表達式在其他。我不知道如何在Linq表達式中處理Func。也許智能感知是錯誤的?

表達聚合源是我最大的問題。通過觀察:

...b.Sum(c => c.Amount) 

我會假定乙方應IGrouping(的「選擇」 ParameterExpression),這應該是求和的來源,但不會編譯。 我不知道還有什麼要嘗試?

這裏是選擇表達式應該怎麼過去的樣子:

Expression Select = Expression.Call(
    typeof(Queryable), 
    "Select", 
    new Type[] { typeof(IGrouping<int, Document>), typeof(decimal?) }, 
    GroupBy, 
    Expression.Lambda(sum, peg)); 

但我甚至無法達到,因爲失敗「和」表達了這一點。

任何指針,將不勝感激。

Regards,

+0

只是好奇什麼一個恆定的分組點? – dasblinkenlight

+1

我需要在相同的數據在同一時間幾個聚合,所以它是唯一的方式與單數據庫命中 –

回答

1

Intellisense是好的。讓看到:

db.Documents.GroupBy(a => 1).Select(b => b.Sum(c => c.Amount) }); 

(1)db.Documents類型是IQueryable<Document>
(2)a類型是Document
(3)db.Documents.GroupBy(a => 1)類型是IQueryable<IGrouping<int, Document>>
(4)b類型是IGrouping<int, Document>which in turn isIEnumerable<Document>
( 5)c類型是Document

這也意味着GroupBySelect方法來自Queryable,而Sum來自Enumerable

怎麼區分Func<...>Expression<Func<...>>裏面的MethodCall表達式,規則很簡單。在這兩種情況下,您都使用Expression.Lambda<Func<...>>創建Expression<Func<...>>,然後如果呼叫需要Func<...>,您直接傳遞它,並且如果方法預計爲Expression<Func<...>>,則您將其包裝爲Expression.Quote

有了這樣說,讓構建示例查詢表達式:

var query = db.Documents.AsQueryable(); 
// query.GroupBy(a => 1) 
var a = Expression.Parameter(typeof(Document), "a"); 
var groupKeySelector = Expression.Lambda(Expression.Constant(1), a); 
var groupByCall = Expression.Call(typeof(Queryable), "GroupBy", 
    new Type[] { a.Type, groupKeySelector.Body.Type }, 
    query.Expression, Expression.Quote(groupKeySelector)); 
// c => c.Amount 
var c = Expression.Parameter(typeof(Document), "c"); 
var sumSelector = Expression.Lambda(Expression.PropertyOrField(c, "Amount"), c); 
// b => b.Sum(c => c.Amount) 
var b = Expression.Parameter(groupByCall.Type.GetGenericArguments().Single(), "b"); 
var sumCall = Expression.Call(typeof(Enumerable), "Sum", 
    new Type[] { c.Type }, 
    b, sumSelector); 
// query.GroupBy(a => 1).Select(b => b.Sum(c => c.Amount)) 
var selector = Expression.Lambda(sumCall, b); 
var selectCall = Expression.Call(typeof(Queryable), "Select", 
    new Type[] { b.Type, selector.Body.Type }, 
    groupByCall, Expression.Quote(selector)); 
// selectCall is our expression, let test it 
var result = query.Provider.CreateQuery(selectCall); 
+0

感謝伊萬的詳細答案,還沒有實現總結定義在Enumerable和Queryable,這是正確的在我眼前。 –