如何仅从导航属性中查询一小部分(不包括该属性中的所有数据)?

时间:2018-04-27 18:05:08

标签: c# .net entity-framework .net-core entity-framework-core

我有这个小场景:

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

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

此方案的问题是skiptake发生在内存中(由于发出Include而将大量帖子从数据库加载到内存中)。我只想在我获得用户的第一个查询中查询来自数据库的少量帖子(而不是包括整个帖子)。换句话说,我想以某种方式查询少量的包含数据,而不是全部包含它们。

我怎样才能做到这一点?

P.S。:在我的真实代码中,帖子不是直接在User下,而是在多个子属性中。我只是省略了以保持代码简单,因为我如何只包含一个部分的想法应该仍然相同。

更新

我的真实代码,以便更好地了解情况:

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 :(得分:3)

在此特定情况下,您可以从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将在服务器端发生。

更新:实际结构不会改变概念。由于多对多关系,您只需向后导航并将用户ID过滤器更改为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();