3

我有一个 Group by 表达式,我正在动态创建它以用于 LINQ 查询。目前,要构造表达式,我使用以下代码:

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();

然后,我将 GroupBy 中的 keySelector 用于我的 LINQ 查询。我的问题是,如果我想为这个表达式添加第二个分组条件,比如“salesStage”,我将如何将它添加到这个现有的表达式中?

4

1 回答 1

0

You have a problem, because what the compiler does on a regular GroupBy call is generate a new anonymous type with the properties you define. If the type doesn't exist, we cannot create an expression creating an object of the type.

However, given that you are using this for LINQ-to-Objects, we can use the Tuple<> type to generate the grouping key. Hopefully you do not need to group on more than 8 parameters.

Here is a generic function to generate the grouping function:

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();
}
于 2014-01-15T16:27:02.007 回答