使用EF选择组中的TOP元素

时间:2013-10-29 08:26:59

标签: c# entity-framework linq-to-entities

我有以下实体,基本上UserMessage通过MsgGroup建立了M2M关系。线程(或组)中的第一条消息也有ThreadId=MessageId,其他人共享相同的ThreadId

public class User
{
    public int UserId { get; set; }
    [System.ComponentModel.DataAnnotations.Schema.InverseProperty("Received")]
    public virtual System.Collections.Generic.ICollection<MsgGroup> ReceivedGroups { get; set; }
}

public class Message
{
    public int MessageId { get; set; }
    public int ThreadId { get; set; }
    public virtual System.Collections.Generic.ICollection<MsgGroup> MsgGroups { get; set; }
}

public class MsgGroup
{
    public int MsgGroupId { get; set; }

    public int UserId { get; set; }
    public virtual User Received { get; set; }

    public int MessageId { get; set; }
    public virtual Message Message { get; set; }
}

我想为给定用户分组获取lates消息。特别是以下SQL查询:

select * from messages where MessageId IN (
    SELECT MaxId FROM (
        select ThreadId , MAX(MessageId) as MaxId from messages where ThreadId in (
            select distinct MessageId from msggroups where UserId = 1
        ) 
        GROUP BY ThreadId
    ) AS t1
)

我试过了:

var query = from grp in db.MsgGroups.Where(g => g.UserId == userId)
select new
{
    f = (from msg in db.Messages where (msg.ThreadId == grp.MessageId) select msg).OrderByDescending(m => m.Date).Take(1)
};

然而,它会创建一个非常复杂的查询,其中每个消息字段都有子查询。

有解决方案吗?我也可以在基于方法的格式和查询表达式格式中提出相同的问题(因为我根本无法弄清楚查询表达式格式)

3 个答案:

答案 0 :(得分:0)

我认为您可能正在寻求加入,也许是这样的,这将为您提供特定用户的最新消息,按线程ID排序,您可以在部分包含您想要的邮件数量:

var query = (from grp in db.MsgGroups
    .Join(
        db.Messages,
        g => g.MessageId,
        m => m.ThreadId,
        (g, m) => new { grp = g, mgs = m })
        .Where(x => x.grp.UserId == userId)
    select new
    {
        Message = grp.mgs
    })
    .Take(100)
    .OrderByDescending(t => t.Message.ThreadId);

这将为您提供一组匿名对象,每个对象都有一个名为Message的属性,它是您想要的Message对象。

答案 1 :(得分:0)

使用导航属性,让EF处理SQL连接:

var query = from rg in db.MsgGroups
            where rg.UserId == userId
            group rg by rg.ThreadId into thread
            select new { 
                         Thread = thread.Key, 
                         LastMessage = thread.Select(t => t.Message)
                                             .OrderByDescending(m => m.Date)
                                             .FirstOrDefault()
                       };

这为您提供了用户有消息的每个线程的最后一条消息。

答案 2 :(得分:0)

对于下面感兴趣的任何人,查询完全符合我的要求,但在我的问题中,仅使用JOIN而不是IN来区别对象:

var query = 
from mm in
(
    from grp in db.MsgGroups.Where(g => g.UserId == userId)
    from msg in db.Messages
    where msg.ThreadId == grp.MessageId
    group msg by msg.ThreadId into thr
    select new { t = thr.Key, m = thr.Max(t => t.MessageId) }
)
join omsg in db.Messages
on mm.m equals omsg.MessageId
select new { t = mm.t, m = omsg.Text } ;