【问题标题】:Entity Framework multiple join types with dates, group by and count实体框架具有日期、分组依据和计数的多种连接类型
【发布时间】:2015-09-26 07:20:07
【问题描述】:

我有一个包含 3 个表的聊天系统:Users、RoomJoins(您加入的私人聊天室)和 Chats(带有时间戳的聊天消息列表)。

我想获取给定用户的所有私人房间的列表,然后是他上次登录后收到的新聊天消息的数量。

有效的 sql 查询(也许可以改进,但肯定有效)是:

select a.roomid, b.userid, m.firstpicurl, count(p.roomid) as cuenta from roomjoin a
 inner join roomjoin b
on b.roomid=a.roomid and a.userid<>b.userid
inner join myuser m on m.id=b.userid
 left join
 chat p on p.roomid=a.roomid and p.sentwhen > a.lastseen
 where a.userid=45 and a.active=1
  group by a.roomid, b.userid, m.firstpicurl

这基本上是说:给我所有用户 ID = 45 的房间 ID(私人对话),还有给我发送消息的人的用户 ID,他的照片和发送时间的聊天消息数量 > user.lastseen

结果会是这样的

roomid  userid  firstpicurl  cuenta
1        43     http://...     3
2        37     http://...     0

表示用户 ID 43 自您上次登录以来已向您发送了 3 条消息,而用户 37 没有向您发送任何新消息

现在,我尝试将其转换为 EF,并且我有点让它工作,但问题是我找不到使用 sentwhen > lastseen 日期格式进行查询的方法,因为它不允许这样做。如果我尝试使用 Where 子句,我永远不会得到正确的答案。这是我现有的尝试(没有 sentwhen > lastseen)

from a in db.RoomJoins.Where(c => c.userid == u.id && c.active == true).OrderBy(c => c.roomid)
                                 from b in db.RoomJoins.Where(fp => fp.roomid == a.roomid && fp.userid != a.userid)
                                 from m in db.Users.Where(m => b.userid == m.id)
                                 join p in db.Chats on a.roomid equals p.roomid into j1
                                 from j2 in j1.DefaultIfEmpty()
                                 group j2 by new { a.roomid, b.userid, m.firstpicurl } into g
                                 select new
                                 {
                                     roomid = g.Key.roomid,
                                     userid = g.Key.userid,
                                     firstpicurl = g.Key.firstpicurl,
                                     count = g.Count()
                                 };

所以我的代码似乎可以工作,但它有两个问题: 1)它没有考虑时间戳,我只想要我最后一次之后的消息计数 2)当它应该是 0 时,我得到了 1。所以我会得到这样的东西

roomid  userid  firstpicurl  cuenta
1        43     http://...     3
2        37     http://...     1  <-- this should be 0

有人知道如何实现我想要的吗?

第一个答案: 这似乎可行,但看起来非常复杂。有没有办法让更简单?

from a in db.RoomJoins.Where(c => c.userid == 45 && c.active == true)
                                 from b in db.RoomJoins.Where(fp => fp.roomid == a.roomid && fp.userid != a.userid)
                                 from m in db.Users.Where(m => b.userid == m.id)
                                 from p in db.Chats.Where(p => p.roomid==a.roomid && 
                                            p.sentwhen > a.lastseen).DefaultIfEmpty()
                                 select new
                                 {
                                     roomid = a.roomid,
                                     userid = b.userid,
                                     firstpicurl = m.firstpicurl,
                                     cid = p.Id                                    
                                 } into j1
                                 group j1 by new { j1.roomid, j1.userid, j1.firstpicurl } into g
                                 select new
                                 {
                                     roomid = g.Key.roomid,
                                     userid = g.Key.userid,
                                     firstpicurl = g.Key.firstpicurl,
                                     count = g.Count(e => e.cid!=null)
                                 };

【问题讨论】:

  • 当你在表 p 的 join 语句中添加额外的 join on 约束时会发生什么?就像您在 sql 查询中所做的那样?
  • 无论性能如何,Where 子句毕竟应该始终有效。它不起作用,这意味着您的 LINQ 查询在其他时候是错误的。
  • 我可以给出的一般建议是不要把时间花在将 SQL 查询转换为 EF 上。这只会适得其反,没有额外的好处。我在一个我什至无法控制数据库模式的项目中学会了它,每个查询都花费了 3 倍的时间来编写:一次在熟悉的 sql 中,第二次在 EF 中,第三次正在弄清楚为什么它还是不行。 EF 在简单的模式方面非常出色,并且非常适合更改跟踪。其余的调用 SP。
  • SQL 查询 count(p.roomid) 将计算所有 非空 值。但是g.Count() 将计算所有(包括空值)。因此,在这种情况下,您只需将其修改为仅计算非空值(由于DefaultIfEmpty() 而添加了空值),它应该是这样的count = g.Count(e =&gt; e != null)。这应该有效(关于您在Chats 上使用Where 子句)。
  • 感谢计数提示。这修复了 1 而不是 0,仍然不知道如何在该查询中包含 lastseen。我似乎无法混合 group by、left join 和 where 子句

标签: c# sql-server entity-framework


【解决方案1】:
join p in db.Chats.Where(x  => x.sentwhen > a.lastseen) 
              on a.roomid equals p.roomid into j1

【讨论】:

  • 你不能加入/在哪里/在
  • @JoaquinGrech 你试过了吗?
  • 显然,这是我做的第一件事,复制和粘贴...立即错误,它不理解您在 Where 方法之后放置“on”。你可以自己试试。
  • 这里也一样。你收到了什么信息?
  • 它说“a”在当前上下文中不存在(在 where 子句中)。所以它看不到之前选择的“a”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多