I need to translate the following LINQ query to Dynamic LINQ that accepts several grouping columns based on user input. Basically I have a bunch of dropdownlists that apply
I know it's been a while since this question was posted but I had to deal with a similar problem recently (dynamic grouping by multiple columns selected by user in runtime) so here's my take on it.
Helper function for creating grouping lambdas
static Expression> GetGroupBy( string property )
{
var data = Expression.Parameter( typeof( T ), "data" );
var dataProperty = Expression.PropertyOrField( data, property );
var conversion = Expression.Convert( dataProperty, typeof( object ) );
return Expression.Lambda>( conversion, data );
}
Function for doing the in-memory grouping. Returns groups.
static IEnumerable> Group( IEnumerable ds, params Func[] groupSelectors )
{
Func, Func[], IEnumerable>> inner = null;
inner = ( d, ss ) => {
if ( null == ss || ss.Length == 0 ) {
return new[] { d };
} else {
var s = ss.First();
return d.GroupBy( s ).Select( g => inner( g.Select( x => x ), ss.Skip( 1 ).ToArray() ) ) .SelectMany( x => x );
}
};
return inner( ds, groupSelectors );
}
How would it be used:
String[] columnsSelectedByUser = ... // contains names of grouping columns selected by user
var entries = ... // Force query execution i.e. fetch all data
var groupBys = columnsSelectedByUser.Select( x => GetGroupBy( x ).Compile()).ToArray();
var grouping = Group(entries, groupBys); // enumerable containing groups of entries
Regarding degrading performances, I don't think that's actually a (big) problem. Even if you constructed a grouping SQL dynamically, the query would have to return the same number of rows as a query without the grouping. So although in this approach the grouping is not done by the database, the number of rows returned by forced query execution is the same as it would be for the hypothetical SQL query with grouping criteria. Sure, database would probably outperform in-memory grouping done by the C# code but the amount of traffic depends solely on how many rows (entries
) have to be grouped.