1

プロパティのセットでグループ化する必要があるオブジェクトのリストがあります。これらのプロパティのセットは、実行時にのみ認識されます。DynamicLINQを使用せずにこれを実現したいと思います。Kirk WollによるLambda式へのGroupByの動的追加の次のコードは正常に機能しますが、これにより結果が1つのプロパティでグループ化されます。

 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

これの何が問題なのですか:

複数の列でグループ化

Linq ではなく .GroupBy メソッドを使用した例を次に示します。

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 に答える