LINQクエリで使用するために動的に作成しているGroup by式があります。現在、式を作成するには、次のコードを使用します。
var arg = Expression.Parameter(typeof(T), helper.getName());
var prop = Expression.Property(arg, "customerType");
var body = Expression.Convert(prop, typeof(object));
var lambda = Expression.Lambda<Func<Contact, object>>(body, arg);
var keySelector = lambda.Compile();
次に、LINQクエリのGroupByでkeySelectorを使用します。私の質問は、この式に2番目のグループ化基準を追加したい場合、たとえば「salesStage」と言った場合、それをこの既存の式にどのように追加すればよいでしょうか。
コンパイラが通常のGroupBy
呼び出しで行うのは、定義したプロパティを使用して新しい匿名型を生成するため、問題が発生します。型が存在しない場合、型のオブジェクトを作成する式を作成できません。
ただし、これをLINQ-to-Objectsに使用している場合、 Tuple<>
タイプを使用してグループ化キーを生成できます。うまくいけば、8つを超えるパラメータでグループ化する必要はありません。
以下は、グループ化関数を生成する一般的な関数です。
static Func<T, object> BuildGrouper<T>(IEnumerable<string> properties) {
var arg = Expression.Parameter(typeof(T), helper.getName());
// This is the list of property accesses we will be using
var parameters = properties.Select(propName => Expression.Property(arg, propName)).ToList();
// Find the correct overload of Tuple.Create.
// This will throw if the number of parameters is more than 8!
var method = typeof(Tuple).GetMethods().Where(m => m.Name == "Create" && m.GetParameters().Length == parameters.Count).Single();
// But it is a generic method, we need to specify the types of each of the arguments
var paramTypes = parameters.Select(p => p.Type).ToArray();
method = method.MakeGenericMethod(paramTypes);
// Invoke the Tuple.Create method and return the Func
var call = Expression.Call(null, method, parameters);
var lambda = Expression.Lambda<Func<T, object>>(call, arg);
return lambda.Compile();
}