I have a collection that is IEnumerable<Transaction>
. Transaction has several properties such as TransactionId (Int64), PaymentMethod(string) and TransactionDate(DateTime)
I'd like to be able to accomplish this transactions.GroupBy(x => x.PaymentMethod)
dynamically at run time based on whatever grouping field the user has decided to use.
I found most of the answer I'm looking for in dtb's answer here Linq GroupBy - how to specify the grouping key at runtime?
This works well:
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 );
Except that I don't know the type of the return type of the Func in Expression.Lambda<Func<Transaction, string>>
. It's string in this example, but it might be Int64, decimal, DateTime, etc. I can't use Object as the return type because I might have value types.
I've been reading lots of SO posts and most of them seem to apply to IQueryable and LinqToSQL.
Using the Expression class seems like a good way to accomplish this, but is there a way to do it when I don't know either the name or datatype of my group parameter at compile time?
I appreciate any nudge in the right direction.
Edit:
Using Polity's solution below, I created an extension method that does what I've been trying to do:
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;
}
Thanks to Polity and everyone who answered!