1

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

 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 列表,并能够按多个 keySelector 进行分组。我不知道如何做到这一点。请帮忙。

4

2 回答 2

1

看起来如果我们可以动态生成表达式的等价物: x => new { a = x.Prop1, b = x.Prop2, ... } 那么合理的 LINQ => 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

请注意,我实际上并没有编译和运行这段代码,所以我不能保证上面的一切都是完美的。但是,希望它能让您走上正确的轨道。

于 2012-09-12T00:49:13.650 回答
0

这有什么问题:

按多列分组

这是一个使用 .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;
    }
}
于 2012-09-12T02:40:23.570 回答