将查询封装到导航属性

时间:2013-12-04 10:06:32

标签: c# linq entity-framework

为了使我的顶层更具可读性,我通常会使用扩展方法将长难以阅读的查询封装到像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)'方法,以及此方法无法翻译成商店表达。

问题:

我是否还有其他方法可以将查询封装在导航属性上,就像我使用常规查询一样?

2 个答案:

答案 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) });