1

I have been referring this post to group by using expression tree. Here is my code:

String[] fields = { "DepartmentID", "SkillID" };
var groupLambda = GroupByExpression<Person>(fields);
var query = dbContext.People.GroupBy(groupLambda.Compile());
var queryResult = query.ToList();

Here is the method GroupByExpression which uses solution given in aforesaid post (Thanks Daniel!):

public static 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), "x");
var body = Expression.New(constructor, properties.Select(p => Expression.Property(param, p)));
var expr = Expression.Lambda<Func<TItem, object>>(body, param);
return expr;
}

I want to be able to identify fields in the group by keys with strong names in select part like query.Select(x => new { x.Key.DepartmentID, x.Key.SkillID });

How do I do this?

Community
  • 1
  • 1
Jitendra Gupta
  • 764
  • 1
  • 8
  • 16
  • I hope you read the note of `GenericTypeTea`! What you are doing isn't a `GroupBy` "serverside" but is a `GroupBy` "clientside", because by compiling the expression, you obtain a `Func<,>` – xanatos Mar 18 '15 at 11:16

2 Answers2

3

Now... I won't give you the solution to the question you asked, but I'll try to help you :-)

If you want to do dynamic queries, you should probably use DynamicLinq

With DynamicLinq you can do things like:

IQueryable query = context.YourTable;
var groups = query.GroupBy("new (Field1, Field2)");

I'm rereading your question...

I want to be able to identify fields in the group by keys with strong names in select part like query.Select(x => new { x.Key.DepartmentID, x.Key.SkillID });

You can't. GroupBy in general will return a IGrouping<TKey, TSource>. TKey is dynamic (because you build it based on strings), so you can't "extract" it and pass it to the compiler, so you can't do the select with strong names.

There is a single exception: if you know the types and numbers of the GroupBy TKey then something can be done. So, you gave us:

String[] fields = { "DepartmentID", "SkillID" };

If you always have two int then you can cast your query with:

.Cast<IGrouping<Tuple<int, int>, Person>>()
.Select(x => new { x.Key.DepartmentID, x.Key.SkillID });

Note that, as I've written in a comment, your GroupBy will be executed client-side, and everything after the GroupBy will be executed client-side (where client-side == where your program is vs sql-side == where your sql server is)!

DynamicLinq will solve the problem of executing the query sql-side instead of client-side, but won't solve the problem of strong vs weak naming (after a DynamicLinq you can: A) use .Cast<>() method or B) return a dynamic object/IEnumerable<dynamic>)

xanatos
  • 109,618
  • 12
  • 197
  • 280
0

The syntax you're using new { x.Key.DepartmentID, x.Key.SkillID } constructs an anonymous class at compile time. If you want to create an anonymous class at runtime, see here. However, that won't allow you to "identify fields in the group by keys with strong names". If you want to construct an anonymous class at runtime, but be able to use those names at compile time, I'm afraid that's impossible.

Community
  • 1
  • 1
Shlomo
  • 14,102
  • 3
  • 28
  • 43