Как запросить только небольшую часть из свойства навигации (без включения всех данных в это свойство)? - PullRequest
0 голосов
/ 27 апреля 2018

У меня есть маленький сценарий:

var user = await dbContext.Users
        .Include(u => u.Posts)
    .SingleOrDefaultAsync(u => u.Id == userId);

return user
    .SelectMany(u => u.Posts)
    .Skip(someStartIndex)
    .Take(someCount);

Проблема этого сценария заключается в том, что skip и take происходят в памяти (после загрузки большого количества сообщений из базы данных в память из-за выдачи Include). Я просто хочу запросить небольшое количество этих сообщений из базы данных в первом запросе, где я получаю пользователя (вместо включения целых сообщений). Другими словами, я хочу как-то запросить небольшое количество включенных данных, а не включать их все.

Как мне этого добиться?

P.S .: В моем реальном коде сообщения находятся не непосредственно под пользователем, а в нескольких подвойствах. Я просто опустил это, чтобы сохранить код простым, так как идея о том, как включить только часть, должна оставаться прежней.

UPDATE

Мой реальный код, чтобы лучше понять ситуацию:

public async Task<IEnumerable<Post>> GetPostsFromFollowsAsync(Guid userId, int count, int startIndex)
{
    //TODO rewrite this ugly query!!!
    var user = await dbContext.Users
        .Include(u => u.UserFollowing)
            .ThenInclude(uf => uf.FollowingUser)
                .ThenInclude(u => u.Posts)
                    .ThenInclude(p => p.Writer)
                        .ThenInclude(u => u.Profile)
        .Include(u => u.UserFollowing)
            .ThenInclude(uf => uf.FollowingUser)
                .ThenInclude(u => u.Posts)
                    .ThenInclude(p => p.PostLikes)
        .Include(u => u.UserFollowing)
            .ThenInclude(uf => uf.FollowingUser)
                .ThenInclude(u => u.Posts).
                    ThenInclude(p => p.PostCategories)
                        .ThenInclude(pc => pc.Category)
        .Include(u => u.UserFollowing)
            .ThenInclude(uf => uf.FollowingUser)
                .ThenInclude(u => u.Posts)
                    .ThenInclude(p => p.Comments)
        .SingleOrDefaultAsync(u => u.Id == userId);

    return user
        .UserFollowing
        .Select(uf => uf.FollowingUser)
        .SelectMany(u => u.Posts)
        .Skip(startIndex)
        .Take(count);
}

1 Ответ

0 голосов
/ 27 апреля 2018

В этом конкретном сценарии вы можете запустить запрос с Posts и использовать свойство обратной навигации для фильтрации / дополнительных включений:

var userPosts = await dbContext.Posts
    .Include(p => p.User)
    // other includes ...
    .Where(p => p.User.Id == userId)
    .Skip(someStartIndex)
    .Take(someCount)
    .ToListAsync();

Таким образом, Skip / Take будет происходить на стороне сервера.

Обновление: Фактическая структура не меняет концепцию. Вам просто нужно перейти назад и изменить фильтр идентификатора пользователя на Any из-за отношения «многие ко многим»:

return await dbContext.Posts
    .Include(p => p.Writer) // Parent info
        .ThenInclude(u => u.UserFollowers)
            .ThenInclude(uf => uf.FollowerUser)
    .Include(p => p.Writer) // Child info
        .ThenInclude(u => u.Profile)
    .Include(p => p.PostLikes)
    .Include(p => p.PostCategories)
        .ThenInclude(pc => pc.Category)
    .Include(p => p.Comments)
    .Where(p => p.Writer.UserFollowers.Any(uf => uf.FollowerUser.Id == userId))
    .Skip(startIndex)
    .Take(count)
    .ToListAsync();
...