我可以在 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();
我正在尝试修改通用 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();