LINQ - 编写扩展方法以获取每个组具有最大值的行

ctk*_*ohn 5 c# linq linq-to-sql

我的应用程序经常需要对表进行分组,然后返回该组的最大值.在LINQ中这很容易做到:

myTable.GroupBy(r => r.FieldToGroupBy)
.Select(r => r.Max(s => s.FieldToMaximize))
.Join(
    myTable,
    r => r,
    r => r.FieldToMaximize,
    (o, i) => i)
Run Code Online (Sandbox Code Playgroud)

现在假设我想将其抽象为自己的方法.我试着写这个:

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);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这不能编译: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);
}
Run Code Online (Sandbox Code Playgroud)

现在这个编译.但它在运行时失败:"不支持的重载用于查询运算符'Max'." 这就是我所坚持的:我需要找到将maxKeySelector传递给Max()的正确方法.

有什么建议?我正在使用LINQ to SQL,这似乎有所作为.

Str*_*ior 4

首先,我想指出,您尝试做的事情比您在 LINQ 中想象的还要容易:

myTable.GroupBy(r => r.FieldToGroupBy)
    .Select(g => g.OrderByDescending(r => r.FieldToMaximize).FirstOrDefault())
Run Code Online (Sandbox Code Playgroud)

...这应该会让我们的第二部分的生活变得更轻松一些:

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());
}
Run Code Online (Sandbox Code Playgroud)

关键是,通过将您的组设置为IQueryable,您可以打开一组新的 LINQ 方法,这些方法可以采用实际表达式而不是采用Funcs。这应该与大多数标准 LINQ 提供程序兼容。