C# Linq-具有IEnumerable的动态GroupBy<;T>;

C# Linq-具有IEnumerable的动态GroupBy<;T>;,c#,linq,dynamic,ienumerable,C#,Linq,Dynamic,Ienumerable,我有一个集合是IEnumerable。事务有几个属性,如TransactionId(Int64)、PaymentMethod(string)和TransactionDate(DateTime) 我希望能够在运行时根据用户决定使用的任何分组字段动态地完成这个transactions.GroupBy(x=>x.PaymentMethod) 我在dtb的答案中找到了我想要的大部分答案 这很有效: var arg = Expression.Parameter( typeof( Trans

我有一个集合是
IEnumerable
。事务有几个属性,如TransactionId(Int64)、PaymentMethod(string)和TransactionDate(DateTime)

我希望能够在运行时根据用户决定使用的任何分组字段动态地完成这个
transactions.GroupBy(x=>x.PaymentMethod)

我在dtb的答案中找到了我想要的大部分答案

这很有效:

        var arg = Expression.Parameter( typeof( Transaction ), "transaction" );
        var body = Expression.Property( arg, "PaymentMethod" );
        var lambda = Expression.Lambda<Func<Transaction, string>>( body, arg );
        var keySelector = lambda.Compile();

        var groups = transactions.GroupBy( keySelector );
var arg=Expression.Parameter(typeof(Transaction),“Transaction”);
var body=Expression.Property(arg,“PaymentMethod”);
var lambda=表达式.lambda(body,arg);
var keySelector=lambda.Compile();
var groups=transactions.GroupBy(keySelector);
除了我不知道
Expression.Lambda
中Func的返回类型之外。在本例中它是字符串,但可能是Int64、decimal、DateTime等。我不能使用Object作为返回类型,因为我可能有值类型

我已经阅读了很多SO帖子,其中大多数似乎适用于IQueryable和LinqToSQL

使用Expression类似乎是实现这一点的一个好方法,但是当我在编译时不知道组参数的名称或数据类型时,有没有办法做到这一点

我欣赏任何朝着正确方向的推动

编辑:

使用下面Polity的解决方案,我创建了一个扩展方法,实现了我一直在尝试的功能:

    public static IEnumerable<IGrouping<object, T>> GroupBy<T>( this IEnumerable<T> items, string groupByProperty )
    {

        var arg = Expression.Parameter( typeof(T), "item" );
        var body = Expression.Convert( Expression.Property( arg, groupByProperty ), typeof( object ) );
        var lambda = Expression.Lambda<Func<T, object>>( body, arg );
        var keySelector = lambda.Compile();

        var groups = items.GroupBy( keySelector );
        return groups;
    } 
公共静态IEnumerable GroupBy(此IEnumerable项,字符串groupByProperty)
{
var arg=表达式参数(类型(T),“项”);
var body=Expression.Convert(Expression.Property(arg,groupByProperty),typeof(object));
var lambda=表达式.lambda(body,arg);
var keySelector=lambda.Compile();
变量组=items.GroupBy(keySelector);
返回组;
} 

感谢Polity和所有回答的人

以下代码就是我的意思,希望它们能有所帮助:

static void Main(string[] args)
{

    var query = GroupBy<string>("PaymentMethod");
    foreach (var group in query)
        Console.WriteLine(group.Key + "," + group.Count());
    var query2 = GroupBy<long>("SomeInt64");
    foreach (var group in query2)
        Console.WriteLine(group.Key + "," + group.Count());
}

static IEnumerable<IGrouping<T,Transaction>> GroupBy<T>(string propName)
{
    List<Transaction> transactions = new List<Transaction> 
    {
        new Transaction{ PaymentMethod="AA", SomeDateTime=DateTime.Now.AddDays(-1), SomeDecimal=1.2M, SomeInt64=1000},
        new Transaction{ PaymentMethod="BB", SomeDateTime=DateTime.Now.AddDays(-2), SomeDecimal=3.4M, SomeInt64=2000},
        new Transaction{ PaymentMethod="AA", SomeDateTime=DateTime.Now.AddDays(-1), SomeDecimal=3.4M, SomeInt64=3000},
        new Transaction{ PaymentMethod="CC", SomeDateTime=DateTime.Now.AddDays(2), SomeDecimal=5.6M, SomeInt64=1000},
    };
    var arg = Expression.Parameter(typeof(Transaction), "transaction");
    var body = Expression.Property(arg, propName);
    var lambda = Expression.Lambda<Func<Transaction, T>>(body, arg);
    var keySelector = lambda.Compile();

    var groups = transactions.GroupBy(keySelector);
    return groups;
}

    class Transaction
    {
        public string PaymentMethod { get; set; }
        public Int64 SomeInt64 { get; set; }
        public decimal SomeDecimal { get; set; }
        public DateTime SomeDateTime { get; set; }
    }
static void Main(字符串[]args)
{
var query=GroupBy(“付款方式”);
foreach(查询中的var组)
Console.WriteLine(group.Key+,“+group.Count());
var query2=GroupBy(“SomeInt64”);
foreach(查询2中的var组)
Console.WriteLine(group.Key+,“+group.Count());
}
静态IEnumerable GroupBy(字符串propName)
{
列表事务=新列表
{
新事务{PaymentMethod=“AA”,SomeDateTime=DateTime.Now.AddDays(-1),SomeDecimal=1.2M,SomeInt64=1000},
新事务{PaymentMethod=“BB”,SomeDateTime=DateTime.Now.AddDays(-2),SomeDecimal=3.4M,SomeInt64=2000},
新事务{PaymentMethod=“AA”,SomeDateTime=DateTime.Now.AddDays(-1),SomeDecimal=3.4M,SomeInt64=3000},
新事务{PaymentMethod=“CC”,SomeDateTime=DateTime.Now.AddDays(2),SomeDecimal=5.6M,SomeInt64=1000},
};
var arg=Expression.Parameter(typeof(Transaction),“Transaction”);
var body=Expression.Property(arg,propName);
var lambda=表达式.lambda(body,arg);
var keySelector=lambda.Compile();
var groups=transactions.GroupBy(keySelector);
返回组;
}
类事务
{
公共字符串PaymentMethod{get;set;}
公共Int64 SomeInt64{get;set;}
公共十进制SomeDecimal{get;set;}
public DateTime SomeDateTime{get;set;}
}

跟进ojlovecd的答案

根据一位提问者的说法,他需要运行时的功能。泛型和运行时并不是最简单的组合。但这没有问题,因为您可以将返回值作为对象进行威胁,这使得ojlovecd提供的方法的非泛型变体类似于:

static IEnumerable<IGrouping<object,Transaction>> GroupBy(string propName) 
{ 
    List<Transaction> transactions = new List<Transaction>  
    { 
        new Transaction{ PaymentMethod="AA", SomeDateTime=DateTime.Now.AddDays(-1), SomeDecimal=1.2M, SomeInt64=1000}, 
        new Transaction{ PaymentMethod="BB", SomeDateTime=DateTime.Now.AddDays(-2), SomeDecimal=3.4M, SomeInt64=2000}, 
        new Transaction{ PaymentMethod="AA", SomeDateTime=DateTime.Now.AddDays(-1), SomeDecimal=3.4M, SomeInt64=3000}, 
        new Transaction{ PaymentMethod="CC", SomeDateTime=DateTime.Now.AddDays(2), SomeDecimal=5.6M, SomeInt64=1000}, 
    }; 
    var arg = Expression.Parameter(typeof(Transaction), "transaction"); 
    var body = Expression.Convert(Expression.Property(arg, propName), typeof(object)); 
    var lambda = Expression.Lambda<Func<Transaction, object>>(body, arg); 
    var keySelector = lambda.Compile(); 

    var groups = transactions.GroupBy(keySelector); 
    return groups; 
} 
静态IEnumerable GroupBy(字符串propName)
{ 
列表事务=新列表
{ 
新事务{PaymentMethod=“AA”,SomeDateTime=DateTime.Now.AddDays(-1),SomeDecimal=1.2M,SomeInt64=1000},
新事务{PaymentMethod=“BB”,SomeDateTime=DateTime.Now.AddDays(-2),SomeDecimal=3.4M,SomeInt64=2000},
新事务{PaymentMethod=“AA”,SomeDateTime=DateTime.Now.AddDays(-1),SomeDecimal=3.4M,SomeInt64=3000},
新事务{PaymentMethod=“CC”,SomeDateTime=DateTime.Now.AddDays(2),SomeDecimal=5.6M,SomeInt64=1000},
}; 
var arg=Expression.Parameter(typeof(Transaction),“Transaction”);
var body=Expression.Convert(Expression.Property(arg,propName),typeof(object));
var lambda=表达式.lambda(body,arg);
var keySelector=lambda.Compile();
var groups=transactions.GroupBy(keySelector);
返回组;
} 

如果您在编译时不知道目标类型,则必须构造委托类型并使用
Expression.Call()
,最后使用
DynamicInvoke()
执行
GroupBy

var arg = Expression.Parameter(typeof(Transaction), "transaction");
var body = Expression.Property(arg, "PaymentMethod");

var delegateType = typeof(Func<,>).MakeGenericType(typeof(Transaction), body.Type);
var lambda = Expression.Lambda(delegateType, body, arg);
var source = Expression.Parameter(typeof(IEnumerable<Transaction>), "source");
var groupByExpression = Expression.Call(typeof(Enumerable), "GroupBy", 
                                        new Type[] { typeof(Transaction), body.Type }, 
                                        source, lambda);
var groupByLambda = Expression.Lambda(groupByExpression, source).Compile();

var groups = groupByLambda.DynamicInvoke(transactions);
var arg=Expression.Parameter(typeof(Transaction),“Transaction”);
var body=Expression.Property(arg,“PaymentMethod”);
var delegateType=typeof(Func).MakeGenericType(typeof(Transaction),body.Type);
var lambda=Expression.lambda(delegateType,body,arg);
var source=Expression.Parameter(typeof(IEnumerable),“source”);
var groupByExpression=Expression.Call(typeof(可枚举),“GroupBy”,
新类型[]{typeof(事务),body.Type},
来源,lambda);
var groupByLambda=Expression.Lambda(groupByExpression,source.Compile();
var group=groupbyrambda.DynamicInvoke(事务);

由于此时您无法使用任何其他Linq扩展方法而不将其转换为表达式(至少据我所知),因此其好处是值得怀疑的,因此我个人可能会选择其他选项之一。

您能将这些代码转换为通用方法a吗