为了使我的顶层更具可读性,我通常会使用扩展方法将长难以阅读的查询封装到像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&lt;&gt; 或 ICollection&lt;&gt; 时,我在运行查询时遇到以下异常:
LINQ to Entities无法识别方法'System.Collections.Generic.IEnumerable'1 [Match] By(System.Collections.Generic.ICollection`1 [Match],Period)'方法,以及此方法无法翻译成商店表达。
问题:
我是否还有其他方法可以将查询封装在导航属性上,就像我使用常规查询一样?
答案 0 :(得分:0)
您需要为每种类型添加扩展方法:
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);
}
编译器将在编译时选择最合适的。
答案 1 :(得分:0)
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) });