我有这个小场景:
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。:在我的真实代码中,帖子不是直接在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);
}
答案 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();