2014-01-15 2 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.GroupBy(lambda.Compile()); 

... 나는 그것에을 변경할 수 있다고 예상있는 ...

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

...로 확정 ...

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

...하지만 불행하게도 그럴 것 같지 않습니다. 동적 람다를 사용하는 GroupBy는 예외를주지 않으며, 단지 아무것도 그룹화하지 않고 모든 요소를 ​​반환합니다.

내가 뭘 잘못하고 있니? 어떤 도움을 주시면 감사하겠습니다. 미리 감사드립니다.

+0

, 무엇 모습 않습니다

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

는 다음과 같이 호출하려면? – Servy

+0

@Servy 이렇게 : {x => new Dictionary'2() {Void Add (System.String, System.Object) ("Shift", Convert (x.Shift)), Void Add (System.String, System. Object) ("Section", Convert (x.Section))}} –

+0

최종 결과가 사전이라는 사실을 알면 안되며 사전에 그룹화하면 그 내용이 아닌 사전의 참조. 결과가 분명해진다. – Servy

답변

3

그 람다 식은 그룹 필드의 사전을 만듭니다.
Dictionary<TKey, TValue>Equals()GetHashCode()을 구현하지 않으므로 참조 평등으로 그룹화합니다.
항상 새로운 사전을 반환하므로 각 항목마다 고유 한 그룹이 생깁니다.

균등 가치를 위해 Equals()GetHashCode()을 올바르게 구현하는 유형을 만들려면이를 변경해야합니다.
일반적으로 컴파일러에서 익명 형식을 생성하게합니다. 그러나 컴파일 타임에 타입 시그니처를 알지 못하기 때문에 여기서 할 수는 없습니다.

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

유용한 답변을 주셔서 감사합니다. 해결책을 찾는데 도움이되었습니다. 별도의 답변으로 게시했습니다. 아마도 여러분이 작성한 샘플 코드가 괄호를 빠뜨리고 변환 오류를내는 것을 알고 싶을 것입니다. –

4

This post 선택하고 GROUPBY 모두에 사용할 수있는 표현 기능을 보여줍니다
대신, Tuple<...> 구성 할 수 있습니다. 희망은 다른 사람들을 돕는다! 당신이 그것을 컴파일하기 전에, 생성 된 표현을 인쇄 할 때

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

그룹화 후 추가 선택을위한 키를 어떻게 식별 할 수 있습니까? 감사. –

+0

이 방법을 시도하지는 않았지만 유창한 방법으로 작동하지 않는 이유는 무엇입니까? students.GroupBy (lambda1.Compile()). (lambda2.Compile())을 선택하십시오. 표현 함수는 GroupBy와 [Select] 모두에 대해 작동 할 수 있습니다 (http://stackoverflow.com/questions/9000753/how-can-i-create-a-dynamic-multi-property-select-on-an-ienumerablet- at-runtime/9001249 # 9001249). –

관련 문제