linq - 带分组和排序的左连接

标签 linq entity-framework linq-to-entities entity-framework-4.1

音乐家写歌。歌曲在空中播放。

我有数据库表“音乐家”、“歌曲”和“AirTimes”。 AirTimes 表条目保存有关哪首歌曲在哪一天播放以及播放了多少分钟的信息。

我有与表相对应的 Musician、Song、AirTime 类。这些类具有指向其他实体的导航属性。下面的箭头代表导航。

音乐家<-->歌曲<-->播放时间

从数据库中,我必须检索所有音乐家以及他/她的歌曲获得播放时间的日期。另外,我想显示特定日期播放的歌曲数量以及该日期播放的分钟数。

在 Microsoft SQL 中,我会按如下方式执行:

select 
    dbo.Musicians.LastName
  , dbo.AirTimes.PlayDate
  , count(dbo.AirTimes.PlayDate) as 'No. of entries'
  , sum(dbo.AirTimes.Duration) as 'No. of minutes'
from dbo.Musicians
  left outer join dbo.Songs
  on dbo.Musicinas.MusicianId = dbo.Songs.MusicianId
    left outer join dbo.AirTimes
    on dbo.Songs.SongId = dbo.AirTimes.SongId
    and '2014-07-01T00:00:00' <= dbo.AirTimes.PlayDate 
    and dbo.AirTimes.PlayDate <= '2014-07-31T00:00:00'
group by 
    dbo.Musicians.LastName
  , dbo.AirTimes.PlayDate
order by
    dbo.Musicians.LastName
  , dbo.AirTimes.PlayDate

任何人都可以将其“翻译”为 linq-to-entitese 吗?

2012 年 8 月 9 日更新
我无法确认格鲁多夫的计划是否符合我的要求。我用不同的技术完成了事情。尽管如此,我接受他/她的回答。

最佳答案

由于您具有两个方向的导航属性,因此您可以从 AirTimes 开始:

var grpTime = (
    from a in AirTimes
    where a.Date >= firstDate && a.Date < lastDate
    group a by new {a.Song.Musician.LastName, a.Song.Title, a.Date} into grp
    select new {
        grp.Key.LastName,
        grp.Key.Title,
        grp.Key.Date,
        Plays = grp.Count(),
        Seconds = grp.Sum(x => x.Duration)
    }
);

或来自音乐家:

var grpMus = (
    from m in Musicians
    from s in m.Songs
    from p in s.Plays
    where p.Date >= firstDate && p.Date < lastDate
    group p by new {m.LastName, s.Title, p.Date} into grp
    select new {
        grp.Key.LastName,
        grp.Key.Title,
        grp.Key.Date,
        Plays = grp.Count(),
        Seconds= grp.Sum(x => x.Duration)
    }
);

编辑: 要显示所有音乐家,包括那些没有播放时间的音乐家,您可以使用另一个级别的分组 - 第一步,您计算每首歌曲+每天的总数,然后将它们与歌曲的作者分组。它可能可以直接与数据库一起工作,但我没有找到有效的方法来做到这一点。然而。 ;) 通过代码,原始的 AirTimes 结果被更改为返回音乐家而不是他的姓氏,然后加入到所有音乐家的列表中:

//Airtimes for musicians
var grpAir = (
    from a in AirTimes
    where a.Date >= firstDate && a.Date < lastDate
    group a by new {a.Song.Musician, a.Date} into grp
    select new {
        //Musician instead of his LastName for joining. Id would work too
        grp.Key.Musician,
        //grp.Key.Musician.LastName,
        Date=grp.Key.Date,
        Plays = grp.Count(),
        Secs = grp.Sum(x => x.Duration)
    }
);

var res = (
    from m in Musicians
    join g in grpAir on m equals g.Musician into g2
    from g in g2.DefaultIfEmpty()
    orderby m.LastName
    select new {
        m.LastName,
        Date = (g==null ? null : g.Date),
        Plays = (g==null ? 0 : g.Plays),
        Secs = (g==null ? 0 : g.Secs)
    }
);

您可以在 https://gist.github.com/3236238 找到更完整的 LINQPad 示例。

关于linq - 带分组和排序的左连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11773049/

相关文章:

c# - Linq to Entities Query 中的 .Include() .Where() .Select()

c# - 如何在使用 LINQ to Entities 和辅助方法时保持 DRY?

c# - Linq 选择特定索引处的数组值

c# - Linq 中的空引用异常

c# - 如何使用 Linq 对列表顺序进行排序?

vb.net - 使用 Linq VB.NET 选择组中的最大日期项目

c# - 使用 EF 查询的最佳方式

c# - 使用 LINQ 检查数据库中是否存在表

c# - 从 EntityFramework 表返回 Dictionary<int,List<Int>>

c# - 如何在没有调用的情况下合并两个 C# Lambda 表达式?