Загрузка самой последней связанной записи объекта в список объектов в одном запросе - PullRequest
1 голос
/ 06 ноября 2011

Я использую Entity Framework 4.1 с POCO и DbContext, без прокси, без отложенной загрузки.

Я очень новичок в 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 также является FK для Users.Id.

У каждого пользователя может быть много статусов и много друзей

Надеюсь, структура базы данных ясна.

Что я пытаюсь сделатьсделать, это получить список пользователей, которые являются моими друзьями и их последний статус.

В 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 Ответ

1 голос
/ 06 ноября 2011

Единственный способ получить это в одном запросе к базе данных - проекция:

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> в качестве типа результата запроса.

...