我可以在 C# LINQ 中创建一个通用的 GroupBy() 吗?

Can I create a generic GroupBy() in C# LINQ?

我正在尝试修改通用 IQueryable 扩展方法以执行 GroupBy 操作。我的方法在动态定义的列上执行通用 Where:

        public static IQueryable<TEntity> WhereById<TEntity, TKey>(
                       this IQueryable<TEntity> query, TKey value, string colName)
            where TEntity : class
        {
            var param = Expression.Parameter(typeof(TEntity), "e");
            var propAccess = Expression.PropertyOrField(param, colName);
            var valExpr = Expression.Constant(value);
   
            BinaryExpression predicate;
            predicate = Expression.Equal(propAccess, valExpr);
            var predicateLambda = Expression.Lambda<Func<TEntity, bool>>(predicate, param);
            return query.Where(predicateLambda);
        }

这完美地工作在:

IQueryable<TEntity> entities = _crudApiDbContext.Set<TEntity>()
                    .WhereById<TEntity, int>(id, selectField);

现在我需要一个通用的 GroupBy()。我正在尝试以下操作:

        public static IQueryable<TEntity> GroupBy<TEntity>(this IQueryable<TEntity> query, string colName)
            where TEntity : class
        {
            var param = Expression.Parameter(typeof(TEntity), "e");
            var propAccess = Expression.PropertyOrField(param, colName);

            BinaryExpression predicate;
            predicate = Expression.XXX(propAccess);  <=what should XXX be?
            var predicateLambda = Expression.Lambda<Func<TEntity, int>>(propAccess);
            return (IQueryable<TEntity>)query.GroupBy(predicateLambda);
        }

我想我的问题是 XXX 应该是什么?或者可能因为 GroupBy() 是一种扩展方法,所以方法需要有所不同?

更新 对于我为什么需要这个有很多兴趣——我正在构建一个通用的 Blazor 表单以进行分面浏览。所以我有一个用于过滤和排序搜索的通用方法,但我还需要知道某些带有选项的列,在应用搜索条件后还剩下多少选项。为此,我将使用应用了搜索条件的 IQueryable 对这些列中的每一列执行 GroupBy。分面搜索示例:

这是如何按动态字段进行分组的解决方案。由于我们不知道分组键的类型,我决定将其设为 object.

public static class QueryableExtensions
{
    static Expression MakePropPath(Expression objExpression, string path)
    {
        return path.Split('.').Aggregate(objExpression, Expression.PropertyOrField);
    }

    public static IQueryable<IGrouping<object, TEntity>> GroupBy<TEntity>(this IQueryable<TEntity> query, string colName)
        where TEntity : class
    {
        var param      = Expression.Parameter(typeof(TEntity), "e");
        var propAccess = MakePropPath(param, colName);

        var keyLambda = Expression.Lambda(Expression.Convert(propAccess, typeof(object)), param);

        var groupCall = Expression.Call(typeof(Queryable), nameof(Queryable.GroupBy),
            new[] { typeof(TEntity), typeof(object) }, query.Expression,
            keyLambda);

        return query.Provider.CreateQuery<IGrouping<object, TEntity>>(groupCall);
    }
}

你的用法很简单:

var result = query.GroupBy("Genre")
    .Select(g => new 
    {
        g.Key,
        Count = g.Count()
    })
    .ToList();