编写一个扩展方法来获取每个组中具有最大值的行
本文关键字:最大值 获取 一个 方法 扩展 | 更新日期: 2023-09-27 18:06:08
我的应用程序经常需要对一个表进行分组,然后返回该组中具有最大值的行。这在LINQ中很容易做到:
myTable.GroupBy(r => r.FieldToGroupBy)
.Select(r => r.Max(s => s.FieldToMaximize))
.Join(
myTable,
r => r,
r => r.FieldToMaximize,
(o, i) => i)
现在假设我想把它抽象成它自己的方法。我试着这样写:
public static IQueryable<TSource>
SelectMax<TSource, TGroupKey, TMaxKey>(
this IQueryable<TSource> source,
Expression<Func<TSource, TGroupKey>> groupKeySelector,
Expression<Func<TSource, TMaxKey>> maxKeySelector)
where TMaxKey : IComparable
{
return source
.GroupBy(groupKeySelector)
.Join(
source,
g => g.Max(maxKeySelector),
r => maxKeySelector(r),
(o, i) => i);
}
不幸的是,这不能编译:maxKeySelector是一个表达式(所以你不能在r上调用它,你甚至不能把它传递给Max。所以我试着重写,使maxKeySelector一个函数,而不是一个表达式:
public static IQueryable<TSource>
SelectMax<TSource, TGroupKey, TMaxKey>(
this IQueryable<TSource> source,
Expression<Func<TSource, TGroupKey>> groupKeySelector,
Func<TSource, TMaxKey> maxKeySelector)
where TMaxKey : IComparable
{
return source
.GroupBy(groupKeySelector)
.Join(
source,
g => g.Max(maxKeySelector),
r => maxKeySelector(r),
(o, i) => i);
}
现在编译。但它在运行时失败:"查询操作符'Max'使用了不支持的过载。"这就是我所坚持的:我需要找到正确的方法来传递maxKeySelector到Max()。
有什么建议吗?我正在使用LINQ to SQL,这似乎有所不同。
首先,我想指出的是,在LINQ:中,您尝试做的事情甚至比您想象的要容易。 …这应该会让我们的第二部分更容易一些: 关键在于,通过使您的组成为myTable.GroupBy(r => r.FieldToGroupBy)
.Select(g => g.OrderByDescending(r => r.FieldToMaximize).FirstOrDefault())
public static IQueryable<TSource>
SelectMax<TSource, TGroupKey, TMaxKey>(
this IQueryable<TSource> source,
Expression<Func<TSource, TGroupKey>> groupKeySelector,
Expression<Func<TSource, TMaxKey>> maxKeySelector)
where TMaxKey : IComparable
{
return source
.GroupBy(groupKeySelector)
.Select(g => g.AsQueryable().OrderBy(maxKeySelector).FirstOrDefault());
}
IQueryable
,您打开了一组新的LINQ方法,这些方法可以接受实际表达式,而不是接受Func
。
非常有趣。有时候,"动态"可能会让你在开发和运行时执行上付出比其价值更多的代价。尽管如此,下面是最简单的:
public static IQueryable<Item> _GroupMaxs(this IQueryable<Item> list)
{
return list.GroupBy(x => x.Family)
.Select(g => g.OrderByDescending(x => x.Value).First());
}
还有,这里是最动态的方法:
public static IQueryable<T> _GroupMaxs<T, TGroupCol, TValueCol>
(this IQueryable<T> list, string groupColName, string valueColName)
{
// (x => x.groupColName)
var _GroupByPropInfo = typeof(T).GetProperty(groupColName);
var _GroupByParameter = Expression.Parameter(typeof(T), "x");
var _GroupByProperty = Expression
.Property(_GroupByParameter, _GroupByPropInfo);
var _GroupByLambda = Expression.Lambda<Func<T, TGroupCol>>
(_GroupByProperty, new ParameterExpression[] { _GroupByParameter });
// (x => x.valueColName)
var _SelectParameter = Expression.Parameter(typeof(T), "x");
var _SelectProperty = Expression
.Property(_SelectParameter, valueColName);
var _SelectLambda = Expression.Lambda<Func<T, TValueCol>>
(_SelectProperty, new ParameterExpression[] { _SelectParameter });
// return list.GroupBy(x => x.groupColName)
// .Select(g => g.OrderByDescending(x => x.valueColName).First());
return list.GroupBy(_GroupByLambda)
.Select(g => g.OrderByDescending(_SelectLambda.Compile()).First());
}
如您所见,我在扩展方法前面加了下划线。当然,您不需要这样做。你就照我说的做吧。
你可以这样调用它:
public class Item
{
public string Family { get; set; }
public int Value { get; set; }
}
foreach (Item item in _List
.AsQueryable()._GroupMaxs<Item, String, int>("Family", "Value"))
Console.WriteLine("{0}:{1}", item.Family, item.Value);
祝你好运!