使用Linq表达式的多个Group By键选择器

时间:2012-09-11 20:55:18

标签: c# linq

我有一个对象列表,需要按一组属性进行分组。这些属性集仅在运行时才知道。我想在不使用Dynamic LINQ的情况下完成此任务。以下代码来自Dynamically Adding a GroupBy to a Lambda Expression的Kirk Woll工作正常,但是这会将结果分组为一个属性。

 public static IQueryable<IGrouping<TColumn, T>> DynamicGroupBy<T, TColumn>(IQueryable<T> source, string column)
    {
        PropertyInfo columnProperty = typeof(T).GetProperty(column);
        var sourceParm = Expression.Parameter(typeof(T), "x");
        var propertyReference = Expression.Property(sourceParm, columnProperty);
        var groupBySelector = Expression.Lambda<Func<T, TColumn>>(propertyReference, sourceParm);

        return source.GroupBy(groupBySelector);

    }

我想传递一个columnNames列表,并能够通过多个keySelectors进行分组。我不知道如何做到这一点。请帮忙。

2 个答案:

答案 0 :(得分:1)

似乎我们可以动态生成表达式的等价物:     x =&gt; new {a = x.Prop1,b = x.Prop2,...} 然后合理的LINQ =&gt; SQL提供程序可能会生成您想要的SQL。动态生成一个新的匿名类型似乎很难,但我们可以利用匿名类型是通用的来重用它。

// at the top of your class
private static readonly object AnonymousObjectWithLotsOfProperties = new {
    a = 1,
    b = 2,
    ...
};

// then in your method
// (1) first get the list of properties represented by the string you passed in (I assume you know how to do this via reflection)
var props = typeof(T).GetProperties().Where(...);
var propTypes = props.Select(pi => pi.PropertyType).ToList();

// (2) now generate the correctly genericized anonymous type to use
var genericTupleType = AnonymousObjectWithLotsOfProperties.GetType()
    .GetGenericTypeDefinition();
// for generic args, use the prop types and pad with int
var genericArguments = propTypes.Concat(Enumerable.Repeat(typeof(int), genericTupleType.GetProperties().Length - propTypes.Count))
    .ToArray();
var tupleType = genericTupleType.MakeGenericType(genericArguments);

// (3) now we have to generate the x => new { ... }  expression
// if you inspect "System.Linq.Expressions.Expression<Func<object>> exp = () => new { a = 2, b = 3 };"
// in the VS debugger, you can see that this is actually a call to a constructor
// on the anonymous type with 1 argument per property
var tParameter = Expression.Parameter(typeof(T));
// create the expression
var newExpression = Expression.New(
    constructor: tupleType.GetConstructors().Single(), // get the one constructor we need
    // the arguments are member accesses on the parameter of type T, padded with 0
    arguments: props.Select(pi => Expression.MakeMemberAccess(tParameter, pi))
        .Concat(Enumerable.Repeat(Expression.Constant(0), genericTupleType.GetProperties().Length - propTypes.Count))
);
// create the lambda: we need an Expression<TDelegate>, which means that we
// need to get the generic factory method from Expression and invoke it
var lambdaGenericMethod = typeof(Expression).GetMethods(BindingFlags.Static | BindingFlags.Public)
    .Single(m => m.IsGenericMethodDefinition);
var lambdaMethod = lambdaGenericMethod.MakeGenericMethod(typeof(Func<,>).MakeGenericType(typeof(T), tupleType));
// reflection for Expression.Lambda(body, parameters)
var lambda = lambdaGenericMethod.Invoke(null, new object[] { newExpression, new[] { tParameter });

// now that we have the expression, we can invoke GroupBy via reflection.
// Of course, that leaves you with an IQueryable<IGrouping<ANON, T>>, which isn't much
// use until you apply some other IQueryable methods to eliminate the ANON type from the
// method signature so you can return it

请注意,我实际上并没有编译并运行此代码,所以我不能保证上面的所有内容都是完美的。但是,希望它会让你走上正确的轨道。

答案 1 :(得分:0)

这有什么问题:

Group By Multiple Columns

以下是使用.GroupBy方法而不是Linq:

的示例
public class Program
{
    static void Main(string[] args)
    {

        var list = new List<SomeType>
            {
                new SomeType("Mr", "Joe", "Bloggs"),
                new SomeType("Mr", "John", "Citizen"),
                new SomeType("Mrs", "Mary", "Mac")
            };

        var list2 = list.GroupBy(by => new { by.Property1, by.Property2 });
    }
}

public class SomeType
{
    public string Property1 { get; private set; }
    public string Property2 { get; private set; }
    public string Property3 { get; private set; }

    public SomeType(string property1, string property2, string property3)
    {
        Property1 = property1;
        Property2 = property2;
        Property3 = property3;
    }
}
相关问题