为了使顶层更具可读性,我通常会制作扩展方法,将难以读取的长查询封装到像db.Matches.By(period)
这样简单的东西中
这种"By"方法看起来像这样:
public static IQueryable<PlayedMatch> By(this IQueryable<PlayedMatch> matches, Period period)
{
return matches.Where(pm => pm.Details.DateTime >= period.Start && pm.Details.DateTime < period.End);
}
问题是,我希望有类似的东西来查询导航属性,所以我可以做这样的事情:
var query = Db.Players.Select( p => new
{
Player = p,
TotalPoints = p.Matches.By(period).Sum(m => m.Points)
});
问题是,首先,导航属性的类型是ICollection<>
。第二,当我将扩展方法更改为使用IEnumerable<>时或ICollection<>运行查询时出现以下异常:
LINQ to Entities无法识别方法"System.Collections.Generic.IEnumerable'1[Match]By(System.Collections.Generic.ICollection`1[Match],Period)",并且此方法无法转换为存储表达式
问题:
有没有其他方法可以像处理普通查询一样,将查询封装在导航属性上?
您需要为每种类型添加一个扩展方法:
public static IQueryable<PlayedMatch> By(this IQueryable<PlayedMatch> matches, Period period)
{
return matches.Where(pm => pm.Details.DateTime >= period.Start && pm.Details.DateTime < period.End);
}
public static ICollection<PlayedMatch> By(this ICollection<PlayedMatch> matches, Period period)
{
return matches.Where(pm => pm.Details.DateTime >= period.Start && pm.Details.DateTime < period.End);
}
public static IEnumerable<PlayedMatch> By(this IEnumerable<PlayedMatch> matches, Period period)
{
return matches.Where(pm => pm.Details.DateTime >= period.Start && pm.Details.DateTime < period.End);
}
编译器将在编译时选择最合适的。
Linq to Entities无法将By
方法转换为sql。如果你把所有的玩家都带到内存中,它就会起作用,因为那样你就可以使用Linq to Objects,它可以与你的C#代码一起工作:
var query = Db.Players
.AsEnumerable //pulls all players into memory
.Select( p => new
{
Player = p,
TotalPoints = p.Matches.By(period).Sum(m => m.Points)
});
但你可能不想为把所有数据都带进内存而付出代价。。。。
如果您想封装长而难读的查询,可以将它们声明为字段。然后你可以做这样的事情:
Func<Bar, bool> NameIsTom = b => b.Name == "Tom";
Foos.Select(f => new { Foo = f, Toms = f.Bars.Where(NameIsTom) });