在单个查询中的实体列表中加载最新的相关实体记录

时间:2011-11-06 12:01:14

标签: sql-server linq entity-framework-4.1 poco


我正在使用带有POCO和DbContext的Entity Framework 4.1,没有代理也没有延迟加载。

我是Entity Framework和LINQ的新手,但到目前为止,使用它的过程非常简单。 我有一些SQL的基本知识,并且首先构建了数据库,然后创建了模型。

我的问题涉及3个表格(我只留下相关内容):

CREATE TABLE [dbo].[Users](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NULL
)

CREATE TABLE [dbo].[UserFriends](
    [UserId] [int] NOT NULL,
    [FriendId] [int] NOT NULL
)

CREATE TABLE [dbo].[UserStatuses](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [UserId] [int] NOT NULL,
    [Message] [nvarchar](max) NOT NULL
)

PK是Usres.Id,UserStatuses.Id,UserFriends.UserId + UserFriends.FriendId。
UserId也是Users.Id的FK。

每个用户都可以拥有许多状态和很多朋友

我希望数据库结构清晰。

我要做的是获取一个朋友用户列表及其最新状态。

在SQL中,它看起来像:

SELECT * FROM Users U 
OUTER APPLY 
(
    SELECT TOP 1 *
    FROM UserStatuses
    WHERE UserId = U.Id
    ORDER BY Id DESC
) S
WHERE U.Id IN (SELECT FriendId FROM UserFriends WHERE UserId = 5)

这将使所有朋友获得'5'及其最新状态消息。 我认为这是最有效的方式(对朋友和用户进行内部联接以及对状态消息进行外部联接),但这不是问题。

我想使用实体框架来做到这一点。 我发现了如何获得我的朋友列表:

var friends = db.UserFriends.Where(f => f.FriendId.Equals(userId)).Select(f => f.User);

但如果我要添加.Include(u => u.UserStatuses)来获取状态,我会得到所有这些,我想只返回最新的。

为了让它发挥作用,我唯一能做的就是:

var friends = db.UserFriends.Where(f => f.FriendId.Equals(userId)).Select(f => f.User);

foreach (Model.User friend in friends)
{
    db.Entry(friend).Collection(f => f.UserStatuses).Query().OrderByDescending(s => s.Id).Take(1).Load();
}

但现在发生的是,对于每个朋友,我都会向数据库生成另一个SQL查询,这似乎是(非常)糟糕的做法。

如何在单个查询中实现? 非常感谢有关如何加载最新插入的相关实体的任何建议。

谢谢,

1 个答案:

答案 0 :(得分:1)

在单个数据库查询中获取此功能的唯一方法是投影:

var friendsWithLastStatus = db.UserFriends
    .Where(f => f.FriendId.Equals(userId))
    .Select(f => new 
    {
        User = f.User,
        LastStatus = f.User.UserStatuses
                           .OrderByDescending(s => s.Id).FirstOrDefault()
    })
    .ToList();

这为您提供了一个匿名类型对象列表,其中每个条目都具有User属性,LastStatus属性具有该用户的最新状态。

如果您愿意,可以投射到命名类型:

public class UserWithLastStatus
{
    public User User { get; set; }
    public UserStatus LastStatus { get; set; }
}

然后在new ...上面的new UserWithLastStatus ...中将List<UserWithLastStatus>替换为{{1}}作为查询的结果类型。