My application frequently needs to group a table, then return the row with the maximum value for that group. This is pretty easy to do in LINQ:
myTable.GroupBy(r => r.FieldToGroupBy)
.Select(r => r.Max(s => s.FieldToMaximize))
.Join(
myTable,
r => r,
r => r.FieldToMaximize,
(o, i) => i)
Now suppose I want to abstract this out into its own method. I tried writing this:
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);
}
Unfortunately this doesn’t compile: maxKeySelector is an expression (so you can’t call it on r, and you can’t even pass it to Max. So I tried rewriting, making maxKeySelector a function rather than an expression:
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);
}
Now this compiles. But it fails at runtime: “Unsupported overload used for query operator ‘Max’.” This is what I’m stuck on: I need to find the right way to pass maxKeySelector into Max().
Any suggestions? I’m using LINQ to SQL, which seems to make a difference.
First of all, I’d like to point out that what you’re trying to do is even easier than you think in LINQ:
… which should make our lives a little easier for the second part:
The key is that by making your group an
IQueryable, you open up a new set of LINQ methods that can take actual expressions rather than takingFuncs. This should be compatible with most standard LINQ providers.