假设我有一个非常简单的实体,如下所示:
public class TestGuy
{
public virtual long Id {get;set;}
public virtual string City {get;set;}
public virtual int InterestingValue {get;set;}
public virtual int OtherValue {get;set;}
}
这个人为的示例对象使用 NHibernate(使用 Fluent)进行映射并且工作正常。
是时候做一些报告了。在这个例子中,“testGuys”是一个已经应用了一些标准的 IQueryable。
var byCity = testGuys
.GroupBy(c => c.City)
.Select(g => new { City = g.Key, Avg = g.Average(tg => tg.InterestingValue) });
这工作得很好。在 NHibernate Profiler 中,我可以看到生成了正确的 SQL,并且结果符合预期。
受我成功的启发,我想让它更灵活。我想让它可配置,以便用户可以获得 OtherValue 和 InterestingValue 的平均值。不应该太难,Average() 的参数似乎是一个 Func(因为在这种情况下值是整数)。十分简单。我不能只创建一个基于某些条件返回 Func 的方法并将其用作参数吗?
var fieldToAverageBy = GetAverageField(SomeEnum.Other);
private Func<TestGuy,int> GetAverageField(SomeEnum someCondition)
{
switch(someCondition)
{
case SomeEnum.Interesting:
return tg => tg.InterestingValue;
case SomeEnum.Other:
return tg => tg.OtherValue;
}
throw new InvalidOperationException("Not in my example!");
}
然后,在其他地方,我可以这样做:
var byCity = testGuys
.GroupBy(c => c.City)
.Select(g => new { City = g.Key, Avg = g.Average(fieldToAverageBy) });
好吧,我以为我能做到。然而,当我列举这一点时,NHibernate 会抛出一个合适的:
Object of type 'System.Linq.Expressions.ConstantExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'.
所以我猜测在幕后,一些转换或强制转换或一些这样的事情正在发生,在第一种情况下接受我的 lambda,但在第二种情况下,NHibernate 无法转换为 SQL。
My question is hopefully simple - how can my GetAverageField function return something that will work as a parameter to Average() when NHibernate 3.0 LINQ support (the .Query() method) translates this to SQL?
Any suggestions welcome, thanks!
EDIT
Based on the comments from David B in his answer, I took a closer look at this. My assumption that Func would be the right return type was based on the intellisense I got for the Average() method. It seems to be based on the Enumerable type, not the Queryable one. That's strange.. Need to look a bit closer at stuff.
The GroupBy method has the following return signature:
IQueryable<IGrouping<string,TestGuy>>
That means it should give me an IQueryable, all right. However, I then move on to the next line:
.Select(g => new { City = g.Key, Avg = g.Average(tg => tg.InterestingValue) });
If I check the intellisense for the g variable inside the new { } object definition, it is actually listed as being of type IGrouping - NOT IQueryable>. This is why the Average() method called is the Enumerable one, and why it won't accept the Expression parameter suggested by David B.
So somehow my group value has apparently lost it's status as an IQueryable somewhere.
Slightly interesting note:
I can change the Select to the following:
.Select(g => new { City = g.Key, Avg = g.AsQueryable<TestGuy>().Average(fieldToAverageBy) });
And now it compiles! Black magic! However, that doesn't solve the issue, as NHibernate now doesn't love me anymore and gives the following exception:
Could not parse expression '[-1].AsQueryable()': This overload of the method 'System.Linq.Queryable.AsQueryable' is currently not supported, but you can register your own parser if needed.
What baffles me is that this works when I give the lambda expression to the Average() method, but that I can't find a simple way to represent the same expression as an argument. I am obviously doing something wrong, but can't see what...!?
I am at my wits end. Help me, Jon Skeet, you're my only hope! ;)