实体框架嵌套投影很慢

时间:2017-03-22 10:03:17

标签: entity-framework entity-framework-6 refactoring

我正在运行查询以获取用户个人资料。该查询获取用户详细信息,以及他们发布的所有评论以及评论中的评论。

这可能是我试图回复太多的情况,但是随着api从移动设备调用,我宁愿在一次网络呼叫中尽可能多地获取而不是多次进行网络呼叫。

目前这会生成一些非常长的sql,大约需要25秒!

有关如何改进它的任何提示,或者投影是否是正确的方法

public UserVM GetUserInfo(string userId, string currentUserId)
{
    var results =
         from u in context.AspNetUsers
         where u.Id == userId
         select new UserVM
         {
             Name = u.UserName, Id = u.Id, ProfilePic = u.ProfilePicUrl, FollowerCount = u.Followers.Count, FollowingCount = u.Following.Count,
             MemberSince = u.RegisteredDate,
             RatingsCount = u.Ratings.Count(x => x.IsDeleted!=true),
             FollowedByCurrentUser = currentUserId != null && u.Followers.Any(x => x.FollowedByUserId == currentUserId && x.UserId == userId),
             reviews = 
               from r in u.Ratings
               where r.IsDeleted != true
               && r.IsDraft != true

               select new RatingVM()
               {
                   ratingId = r.Id,
                   author_name = r.User.UserName,
                   userId = r.UserId,
                   profile_photo_url = r.User.ProfilePicUrl,
                   rating = r.RatingValue,
                   text = r.RatingComment,
                   created = r.Created,
                   likeCount = r.RatingLikes.Count(x => x.IsLiked && x.RatingId == r.Id),
                   likedByCurrentUser = currentUserId != null && r.RatingLikes.Any(x => x.IsLiked && x.RatingId == r.Id && x.UserId == currentUserId),
                   photos = from ri in r.RatingImages
                            select new PhotoVM { Id = ri.Id, width = 0, height = 0, photo_reference = ri.PhotoUrl, isMember = true, googlePlaceId = r.Place.GooglePlaceId, placeName = r.Place.Name },
                   comments = from c in r.Comments
                              where c.IsDeleted != true 
                              select new CommentVM { commentId = c.Id, Created = c.Created, CommentText = c.CommentText, RatingId = r.Id , UserName = c.User.UserName, ProfilePicUrl = c.User.ProfilePicUrl, userId = c.UserId }
               }
         };

    return results.FirstOrDefault();
}



SELECT 
[Project17].[C1] AS [C1], 
[Project17].[UserName] AS [UserName], 
[Project17].[Id] AS [Id], 
[Project17].[ProfilePicUrl] AS [ProfilePicUrl], 
[Project17].[C32] AS [C2], 
[Project17].[C33] AS [C3], 
[Project17].[RegisteredDate] AS [RegisteredDate], 
[Project17].[C34] AS [C4], 
[Project17].[C2] AS [C5], 
[Project17].[C31] AS [C6], 
[Project17].[C4] AS [C7], 
[Project17].[C5] AS [C8], 
[Project17].[C6] AS [C9], 
[Project17].[C7] AS [C10], 
[Project17].[C8] AS [C11], 
[Project17].[C9] AS [C12], 
[Project17].[C10] AS [C13], 
[Project17].[C11] AS [C14], 
[Project17].[C12] AS [C15], 
[Project17].[C13] AS [C16], 
[Project17].[C14] AS [C17], 
[Project17].[C3] AS [C18], 
[Project17].[C15] AS [C19], 
[Project17].[C16] AS [C20], 
[Project17].[C17] AS [C21], 
[Project17].[C18] AS [C22], 
[Project17].[C19] AS [C23], 
[Project17].[C20] AS [C24], 
[Project17].[C21] AS [C25], 
[Project17].[C22] AS [C26], 
[Project17].[C23] AS [C27], 
[Project17].[C24] AS [C28], 
[Project17].[C25] AS [C29], 
[Project17].[C26] AS [C30], 
[Project17].[C27] AS [C31], 
[Project17].[C28] AS [C32], 
[Project17].[C29] AS [C33], 
[Project17].[C30] AS [C34]
FROM ( SELECT 
    [Limit1].[Id] AS [Id], 
    [Limit1].[UserName] AS [UserName], 
    [Limit1].[ProfilePicUrl] AS [ProfilePicUrl], 
    [Limit1].[RegisteredDate] AS [RegisteredDate], 
    [Limit1].[C1] AS [C1], 
    [Limit1].[C2] AS [C2], 
    [UnionAll1].[C1] AS [C3], 
    [UnionAll1].[Id] AS [C4], 
    [UnionAll1].[Id1] AS [C5], 
    [UnionAll1].[C2] AS [C6], 
    [UnionAll1].[C3] AS [C7], 
    [UnionAll1].[UserId] AS [C8], 
    [UnionAll1].[C4] AS [C9], 
    [UnionAll1].[RatingValue] AS [C10], 
    [UnionAll1].[RatingComment] AS [C11], 
    [UnionAll1].[Created] AS [C12], 
    [UnionAll1].[C5] AS [C13], 
    [UnionAll1].[C6] AS [C14], 
    [UnionAll1].[Id2] AS [C15], 
    [UnionAll1].[Id3] AS [C16], 
    [UnionAll1].[C7] AS [C17], 
    [UnionAll1].[C8] AS [C18], 
    [UnionAll1].[PhotoUrl] AS [C19], 
    [UnionAll1].[C9] AS [C20], 
    [UnionAll1].[GooglePlaceId] AS [C21], 
    [UnionAll1].[Name] AS [C22], 
    [UnionAll1].[C10] AS [C23], 
    [UnionAll1].[C11] AS [C24], 
    [UnionAll1].[C12] AS [C25], 
    [UnionAll1].[C13] AS [C26], 
    [UnionAll1].[C14] AS [C27], 
    [UnionAll1].[C15] AS [C28], 
    [UnionAll1].[C16] AS [C29], 
    [UnionAll1].[C17] AS [C30], 
    CASE WHEN ([UnionAll1].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C31], 
    [Limit1].[C3] AS [C32], 
    [Limit1].[C4] AS [C33], 
    [Limit1].[C5] AS [C34]
    FROM   (SELECT TOP (1) 
        @p__linq__4 AS [p__linq__4], 
        @p__linq__5 AS [p__linq__5], 
        [Project3].[Id] AS [Id], 
        [Project3].[UserName] AS [UserName], 
        [Project3].[ProfilePicUrl] AS [ProfilePicUrl], 
        [Project3].[RegisteredDate] AS [RegisteredDate], 
        1 AS [C1], 
        CASE WHEN ((@p__linq__1 IS NOT NULL) AND ( EXISTS (SELECT 
            1 AS [C1]
            FROM [dbo].[Followers] AS [Extent5]
            WHERE ([Project3].[Id] = [Extent5].[UserId]) AND ([Extent5].[FollowedByUserId] = @p__linq__2) AND ([Extent5].[UserId] = @p__linq__3)
        ))) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C2], 
        [Project3].[C1] AS [C3], 
        [Project3].[C2] AS [C4], 
        [Project3].[C3] AS [C5]
        FROM ( SELECT 
            [Project2].[Id] AS [Id], 
            [Project2].[UserName] AS [UserName], 
            [Project2].[ProfilePicUrl] AS [ProfilePicUrl], 
            [Project2].[RegisteredDate] AS [RegisteredDate], 
            [Project2].[C1] AS [C1], 
            [Project2].[C2] AS [C2], 
            (SELECT 
                COUNT(1) AS [A1]
                FROM [dbo].[Ratings] AS [Extent4]
                WHERE ([Project2].[Id] = [Extent4].[UserId]) AND (1 <> [Extent4].[IsDeleted])) AS [C3]
            FROM ( SELECT 
                [Project1].[Id] AS [Id], 
                [Project1].[UserName] AS [UserName], 
                [Project1].[ProfilePicUrl] AS [ProfilePicUrl], 
                [Project1].[RegisteredDate] AS [RegisteredDate], 
                [Project1].[C1] AS [C1], 
                (SELECT 
                    COUNT(1) AS [A1]
                    FROM [dbo].[Followers] AS [Extent3]
                    WHERE [Project1].[Id] = [Extent3].[FollowedByUserId]) AS [C2]
                FROM ( SELECT 
                    [Extent1].[Id] AS [Id], 
                    [Extent1].[UserName] AS [UserName], 
                    [Extent1].[ProfilePicUrl] AS [ProfilePicUrl], 
                    [Extent1].[RegisteredDate] AS [RegisteredDate], 
                    (SELECT 
                        COUNT(1) AS [A1]
                        FROM [dbo].[Followers] AS [Extent2]
                        WHERE [Extent1].[Id] = [Extent2].[UserId]) AS [C1]
                    FROM [dbo].[AspNetUsers] AS [Extent1]
                    WHERE [Extent1].[Id] = @p__linq__0
                )  AS [Project1]
            )  AS [Project2]
        )  AS [Project3] ) AS [Limit1]
    OUTER APPLY  (SELECT 
        CASE WHEN ([Filter10].[Id1] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1], 
        [Project9].[Id] AS [Id], 
        [Project9].[Id] AS [Id1], 
        [Project9].[C1] AS [C2], 
        [Project9].[C2] AS [C3], 
        [Project9].[UserId] AS [UserId], 
        [Project9].[C3] AS [C4], 
        [Project9].[RatingValue] AS [RatingValue], 
        [Project9].[RatingComment] AS [RatingComment], 
        [Project9].[Created] AS [Created], 
        [Project9].[C5] AS [C5], 
        [Project9].[C4] AS [C6], 
        [Filter10].[Id1] AS [Id2], 
        [Filter10].[Id1] AS [Id3], 
        CASE WHEN ([Filter10].[Id1] IS NULL) THEN CAST(NULL AS int) ELSE 0 END AS [C7], 
        CASE WHEN ([Filter10].[Id1] IS NULL) THEN CAST(NULL AS int) ELSE 0 END AS [C8], 
        [Filter10].[PhotoUrl] AS [PhotoUrl], 
        CASE WHEN ([Filter10].[Id1] IS NULL) THEN CAST(NULL AS bit) ELSE cast(1 as bit) END AS [C9], 
        [Filter10].[GooglePlaceId] AS [GooglePlaceId], 
        [Filter10].[Name] AS [Name], 
        CAST(NULL AS int) AS [C10], 
        CAST(NULL AS int) AS [C11], 
        CAST(NULL AS datetime2) AS [C12], 
        CAST(NULL AS varchar(1)) AS [C13], 
        CAST(NULL AS int) AS [C14], 
        CAST(NULL AS varchar(1)) AS [C15], 
        CAST(NULL AS varchar(1)) AS [C16], 
        CAST(NULL AS varchar(1)) AS [C17]
        FROM   (SELECT 
            [Project7].[Id] AS [Id], 
            [Project7].[RatingValue] AS [RatingValue], 
            [Project7].[RatingComment] AS [RatingComment], 
            [Project7].[Created] AS [Created], 
            [Project7].[PlaceId] AS [PlaceId], 
            [Project7].[UserId] AS [UserId], 
            [Limit1].[UserName] AS [C1], 
            N'' AS [C2], 
            [Limit1].[ProfilePicUrl] AS [C3], 
            CASE WHEN ((@p__linq__4 IS NOT NULL) AND ( EXISTS (SELECT 
                1 AS [C1]
                FROM [dbo].[RatingLikes] AS [Extent8]
                WHERE ([Project7].[Id] = [Extent8].[RatingId]) AND ([Extent8].[IsLiked] = 1) AND ([Extent8].[RatingId] = [Project7].[Id]) AND ([Extent8].[UserId] = @p__linq__5)
            ))) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C4], 
            [Project7].[C1] AS [C5]
            FROM ( SELECT 
                [Project6].[Id] AS [Id], 
                [Project6].[RatingValue] AS [RatingValue], 
                [Project6].[RatingComment] AS [RatingComment], 
                [Project6].[Created] AS [Created], 
                [Project6].[PlaceId] AS [PlaceId], 
                [Project6].[UserId] AS [UserId], 
                (SELECT 
                    COUNT(1) AS [A1]
                    FROM [dbo].[RatingLikes] AS [Extent7]
                    WHERE ([Project6].[Id] = [Extent7].[RatingId]) AND ([Extent7].[IsLiked] = 1) AND ([Extent7].[RatingId] = [Project6].[Id])) AS [C1]
                FROM ( SELECT 
                    [Extent6].[Id] AS [Id], 
                    [Extent6].[RatingValue] AS [RatingValue], 
                    [Extent6].[RatingComment] AS [RatingComment], 
                    [Extent6].[Created] AS [Created], 
                    [Extent6].[PlaceId] AS [PlaceId], 
                    [Extent6].[UserId] AS [UserId]
                    FROM [dbo].[Ratings] AS [Extent6]
                    WHERE ([Limit1].[Id] = [Extent6].[UserId]) AND (1 <> [Extent6].[IsDeleted]) AND (1 <> [Extent6].[IsDraft])
                )  AS [Project6]
            )  AS [Project7] ) AS [Project9]
        OUTER APPLY  (SELECT [Extent9].[Id] AS [Id1], [Extent9].[PhotoUrl] AS [PhotoUrl], [Project10].[Name] AS [Name], [Project10].[GooglePlaceId] AS [GooglePlaceId]
            FROM  [dbo].[RatingImages] AS [Extent9]
            LEFT OUTER JOIN  (SELECT 
                [Extent10].[Id] AS [Id], 
                [Extent10].[Name] AS [Name], 
                [Extent10].[GooglePlaceId] AS [GooglePlaceId]
                FROM [dbo].[Places] AS [Extent10]
                WHERE [Project9].[PlaceId] = [Extent10].[Id] ) AS [Project10] ON 1 = 1
            WHERE [Project9].[Id] = [Extent9].[RatingId] ) AS [Filter10]
    UNION ALL
        SELECT 
        2 AS [C1], 
        [Project15].[Id] AS [Id], 
        [Project15].[Id] AS [Id1], 
        [Project15].[C1] AS [C2], 
        [Project15].[C2] AS [C3], 
        [Project15].[UserId] AS [UserId], 
        [Project15].[C3] AS [C4], 
        [Project15].[RatingValue] AS [RatingValue], 
        [Project15].[RatingComment] AS [RatingComment], 
        [Project15].[Created] AS [Created], 
        [Project15].[C5] AS [C5], 
        [Project15].[C4] AS [C6], 
        CAST(NULL AS int) AS [C7], 
        CAST(NULL AS int) AS [C8], 
        CAST(NULL AS int) AS [C9], 
        CAST(NULL AS int) AS [C10], 
        CAST(NULL AS varchar(1)) AS [C11], 
        CAST(NULL AS bit) AS [C12], 
        CAST(NULL AS varchar(1)) AS [C13], 
        CAST(NULL AS varchar(1)) AS [C14], 
        [Join2].[Id2] AS [Id2], 
        [Join2].[Id2] AS [Id3], 
        [Join2].[Created] AS [Created1], 
        [Join2].[CommentText] AS [CommentText], 
        [Project15].[Id] AS [Id4], 
        [Join2].[UserName] AS [UserName], 
        [Join2].[ProfilePicUrl] AS [ProfilePicUrl], 
        [Join2].[UserId] AS [UserId1]
        FROM   (SELECT 
            [Project13].[Id] AS [Id], 
            [Project13].[RatingValue] AS [RatingValue], 
            [Project13].[RatingComment] AS [RatingComment], 
            [Project13].[Created] AS [Created], 
            [Project13].[UserId] AS [UserId], 
            [Limit1].[UserName] AS [C1], 
            N'' AS [C2], 
            [Limit1].[ProfilePicUrl] AS [C3], 
            CASE WHEN ((@p__linq__4 IS NOT NULL) AND ( EXISTS (SELECT 
                1 AS [C1]
                FROM [dbo].[RatingLikes] AS [Extent13]
                WHERE ([Project13].[Id] = [Extent13].[RatingId]) AND ([Extent13].[IsLiked] = 1) AND ([Extent13].[RatingId] = [Project13].[Id]) AND ([Extent13].[UserId] = @p__linq__5)
            ))) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C4], 
            [Project13].[C1] AS [C5]
            FROM ( SELECT 
                [Project12].[Id] AS [Id], 
                [Project12].[RatingValue] AS [RatingValue], 
                [Project12].[RatingComment] AS [RatingComment], 
                [Project12].[Created] AS [Created], 
                [Project12].[UserId] AS [UserId], 
                (SELECT 
                    COUNT(1) AS [A1]
                    FROM [dbo].[RatingLikes] AS [Extent12]
                    WHERE ([Project12].[Id] = [Extent12].[RatingId]) AND ([Extent12].[IsLiked] = 1) AND ([Extent12].[RatingId] = [Project12].[Id])) AS [C1]
                FROM ( SELECT 
                    [Extent11].[Id] AS [Id], 
                    [Extent11].[RatingValue] AS [RatingValue], 
                    [Extent11].[RatingComment] AS [RatingComment], 
                    [Extent11].[Created] AS [Created], 
                    [Extent11].[UserId] AS [UserId]
                    FROM [dbo].[Ratings] AS [Extent11]
                    WHERE ([Limit1].[Id] = [Extent11].[UserId]) AND (1 <> [Extent11].[IsDeleted]) AND (1 <> [Extent11].[IsDraft])
                )  AS [Project12]
            )  AS [Project13] ) AS [Project15]
        INNER JOIN  (SELECT [Extent14].[Id] AS [Id2], [Extent14].[CommentText] AS [CommentText], [Extent14].[Created] AS [Created], [Extent14].[RatingId] AS [RatingId], [Extent14].[IsDeleted] AS [IsDeleted], [Extent14].[UserId] AS [UserId], [Extent15].[UserName] AS [UserName], [Extent15].[ProfilePicUrl] AS [ProfilePicUrl]
            FROM  [dbo].[Comments] AS [Extent14]
            INNER JOIN [dbo].[AspNetUsers] AS [Extent15] ON [Extent14].[UserId] = [Extent15].[Id] ) AS [Join2] ON ([Project15].[Id] = [Join2].[RatingId]) AND (1 <> [Join2].[IsDeleted])) AS [UnionAll1]
)  AS [Project17]
ORDER BY [Project17].[Id] ASC, [Project17].[C31] ASC, [Project17].[C5] ASC, [Project17].[C3] ASC

1 个答案:

答案 0 :(得分:2)

EF6的每个LINQ查询使用一个大SQL查询的加载策略对于加载复杂对象图不是最佳选择。还有其他几种方法可以加载图表。

例如,您可以加载根AspNetUser实体,然后遍历导航属性以构建图形。 EF会根据需要进行延迟加载。一旦实体在上下文中缓存,后续导航就不会导致其他查询。

事实上,如果您预先获取部分或全部相关实体,EF将“缝合”或“修复”您的导航属性。

因此,作为优化,您可以使用一些简单且便宜的查询编写查询,以获取您将需要的实体。

类似

 var user = context.AspNetUsers.Where(u => u.Id == userId).Single();
 var followers = context.Followers.Where(f => f.UserId == userId).ToList();
 var ratings = context.Ratings.Where(f => f.UserId == userId).ToList();
 var ratingIds = ratings.Select(r => r.Id).ToList();
 var ratingLikes = context.RatingLikes.Where(x => ratingIds.Contains(x.RatingId) && x.IsLiked ).ToList();
 var ratingPhotos = context.RatingPhotos.Where(x => ratingIds.Contains(x.RatingId)).ToList();

然后从加载的AspNetUser构建结果,例如

  var u = user;
  var results =
              select new UserVM
                    {
                         Name = u.UserName,
                         Id = u.Id,
                         ProfilePic = u.ProfilePicUrl,
                         FollowerCount = u.Followers.Count, . . .