将查询封装到导航属性



为了使顶层更具可读性,我通常会制作扩展方法,将难以读取的长查询封装到像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) });

最新更新