I have code very much like this:
Context.History.GroupBy(d => new { d.Category, d.Month })
.Select(group => new
{
Category = group.Key.Category,
Month = group.Key.Month,
// Geometric mean:
Value = Math.Exp(group.Average(a => Math.Log(a.Value)))
})
.GroupBy(d => d.Month)
.Select(monthly => new Dto {
Month = monthly.Month,
Average = monthly.Average(a => a.Value)
});
There's a few other places I calculate the geometric mean, so I would like to put that in an extension method, so the line after the comment would look like Value = group.GeometricMean(a => a.Value)
.
If I write a regular extension method on
IEnumerable<T>
, Entity Framework complains that it can't convert it to a query.If I write a method operating on
IQueryable<T>
, I'm informed thatgroup
is, in fact, not anIQueryable<T>
, but rather anIGrouping<TKey, T>
.Trying to call it as
Value = group.AsQueryable().GeometricMean(a => a.Value)
leads to anArgumentException
saying 'Expression of type IGrouping cannot be used for parameter of type IQueryable (Parameter arg0)'.
It seems to me I must somehow turn that IGrouping
into IQueryable
for my extension method to work, but I can't figure out how. There's a few questions on SO that wander near that subject, but none that I've found that are applicable here. It might be that this is simply not supported in EF as of now.
More information: replacing the whole Select
body with some function is a no-go, as the point is to make the geometric mean calculation reusable - the rest is simply not reusable. It needs to be done in SQL, as it's been shown to be far too slow to fetch the data and do the calculation in C#.
Looking at the source of some IQueryable
extension methods, it seems the body of the extension method would build up some expression tree and pass that to source.Provider.Execute
. I think I may already have managed that part, but I've been unable to properly test it due to the above issues.