2014-01-15 58 views
5

要動態生成GroupBy表達式,我試圖構建一個Linq表達式樹。分組的字段是動態的,可以在數量上有所不同。構建GroupBy具有多個字段的表達式樹

我用這個代碼:(!謝謝馬克礫石)

string[] fields = {"Name", "Test_Result"}; 
Type studentType = typeof(Student); 

var itemParam = Expression.Parameter(studentType, "x"); 

var addMethod = typeof(Dictionary<string, object>).GetMethod(
    "Add", new[] { typeof(string), typeof(object) }); 
var selector = Expression.ListInit(
     Expression.New(typeof(Dictionary<string,object>)), 
     fields.Select(field => Expression.ElementInit(addMethod, 
      Expression.Constant(field), 
      Expression.Convert(
       Expression.PropertyOrField(itemParam, field), 
       typeof(object) 
      ) 
     ))); 
var lambda = Expression.Lambda<Func<Student, Dictionary<string,object>>>(
    selector, itemParam); 

的代碼是從this post複製。

它定型與...

var currentItemFields = students.Select(lambda.Compile()); 

...而我希望我可以改變它......

var currentItemFields = students.GroupBy(lambda.Compile()); 

我認爲lambda表達式無非是...

var currentItemFields = students.GroupBy(o => new { o.Name, o.Test_Result }); 

......但不幸的是,似乎並非如此。具有動態lambda的GroupBy不會給出任何例外,它只是不分組並且返回所有元素。

我在這裏做錯了什麼?任何幫助,將不勝感激。提前致謝。

+0

當你打印出生成的表達,編譯它之前,是什麼樣子? – Servy

+0

@Servy像這樣:{x => new Dictionary'2(){Void Add(System.String,System.Object)(「Shift」,Convert(x.Shift)),Void Add(System.String,System。對象)(「Section」,Convert(x.Section))}} –

+0

所以不應該從你看到的那裏明顯知道最終結果是一個字典,並且你知道字典上的分組將根據字典的參考,而不是其內容,使結果清晰。 – Servy

回答

3

該lambda表達式構建分組字段。
Dictionary<TKey, TValue>不執行Equals()GetHashCode(),所以它通過引用相等來對它們進行分組。
由於你總是返回一個新的字典,每個項目都有自己的組。

您需要將其更改爲創建一個類型,該類型正確實現了值爲0的Equals()GetHashCode()
通常,您需要編譯器生成一個匿名類型。但是,你不能這樣做,因爲你不知道編譯時的類型簽名。
相反,你可以構建一個Tuple<...>

Expression.New(
    Type.GetType("System.Tuple`" + fields.Length) 
     .MakeGenericType(fields.Select(studentType.GetProperty), 
    fields.Select(f => Expression.PropertyOrField(itemParam, f)) 
) 
+0

感謝您的有用答案,它幫助我找到了解決方案,正如我在單獨的答案中發佈的那樣。也許你想知道你寫的示例代碼會遺漏括號並給出轉換錯誤。 –

4

This post顯示可以同時用於選擇和進行分組的表達功能。希望它可以幫助別人!

public Expression<Func<TItem, object>> GroupByExpression<TItem>(string[] propertyNames) 
{ 
    var properties = propertyNames.Select(name => typeof(TItem).GetProperty(name)).ToArray(); 
    var propertyTypes = properties.Select(p => p.PropertyType).ToArray(); 
    var tupleTypeDefinition = typeof(Tuple).Assembly.GetType("System.Tuple`" + properties.Length); 
    var tupleType = tupleTypeDefinition.MakeGenericType(propertyTypes); 
    var constructor = tupleType.GetConstructor(propertyTypes); 
    var param = Expression.Parameter(typeof(TItem), "item"); 
    var body = Expression.New(constructor, properties.Select(p => Expression.Property(param, p))); 
    var expr = Expression.Lambda<Func<TItem, object>>(body, param); 
    return expr; 
} 

被稱爲像這樣:

var lambda = GroupByExpression<Student>(fields); 
var currentItemFields = students.GroupBy(lambda.Compile()); 
+0

如何確定分組後按鍵進一步選擇?謝謝。 –

+0

我沒有試過這個,但爲什麼要流利的方式不工作? students.GroupBy(lambda1.Compile())。Select(lambda2.Compile()); 表達式函數可以同時適用於GroupBy和[Select](http://stackoverflow.com/questions/9000753/how-can-i-create-a-dynamic-multi-property-select-on-an-ienumerablet-在運行時/ 9001249#9001249)。 –