2017-03-22 5 views
1

사용자 프로필을 가져 오기 위해 쿼리를 실행하고 있습니다. 이 쿼리는 사용자 세부 정보는 물론 게시 된 모든 검토 및 검토의 주석을 가져옵니다.엔티티 프레임 워크 중첩 투영이 느립니다.

내가 너무 많이 돌아 오려고하는 경우일지도 모르지만 API가 모바일에서 호출되기 때문에 여러 네트워크를 만드는 것보다 한 번의 네트워크 호출에서 가능한 한 많이 얻을 것입니다. 전화.

지금은 정말 긴 SQL을 생성하고 약 25 초 정도 걸립니다!

모든 그것을 개선하는 방법에 대한 팁, 또는 예측 여부도 바로에게 LINQ 쿼리 당 하나의 큰 SQL 쿼리를 사용

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 
+0

무엇 EF 버전은 당신에있다? 생성 된 SQL을 볼 수 있습니까? 쿼리는 정상적으로 보이기 때문에'r.User' 대신'u'를 사용합니다. –

+0

@IvanStoev. 나는 부품을 주석 처리하는 것을 시도해 보았고, 마지막 두 줄 (사진과 설명 설정)이 실행 시간을 몰고가는 것처럼 보입니다. - 어떤 제안이라도? – raklos

+0

분명히 세부 정보 하위 쿼리의 세부 하위 쿼리이지만 적절한 인덱스 (일반적으로 FK 열에서 EF가 생성하는 인덱스)를 사용하면 일반적으로 문제가되지 않으며 어떻게 향상시킬 수 있는지 알지 못합니다. 또한 루트 쿼리는 단일 사용자 용이므로 데이터가 그다지 많지 않아야합니다. 결국 두 부분으로 나눌 수 있지만 (2 db 쿼리 실행), 그렇게하기 전에 현재 생성 된 SQL을보기 좋을 것입니다. –

답변

2

EF6의 로딩 전략을 수행하는 방법은 복잡한 객체 그래프를로드하기위한 최적이 아닌 있습니다. 그래프를로드하는 다른 여러 가지 방법이 있습니다.

예를 들어 루트 AspNetUser 엔터티를로드 한 다음 탐색 속성을 탐색하여 그래프를 작성할 수 있습니다. EF는 필요에 따라 Lazy Load를 수행합니다. 엔티티가 컨텍스트에서 캐시되면 이후의 탐색으로 인해 추가 쿼리가 발생하지 않습니다.

실제로 관련 엔터티의 일부 또는 전체를 미리 가져 오는 경우 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, . . . 
+0

다음을 포함하는 사용자로드를 시도했습니다 (내 VM에 매핑). var user = AspNetUsers . 포함 (x => x.Followers) . 포함 (x => x.Following) . 포함 ("등급. 평가") . 포함 ("Ratings.Place") . 포함 ("Ratings.RatingImages") .Include ("Ratings.Comments") .Include ("Ratings.Comments.User") .Include ("Ratings.RatingLikes") . 싱글 (a => a.Id == userId); ...이게 합리적인 방법 인 것 같니? – raklos

+0

현명하지 않습니다. Includes는 포함 된 테이블의 모든 열을로드합니다. 투영이 더 좋습니다. 데이터베이스 엔진 튜닝 어드바이저를 통해 쿼리를 실행 했습니까? – Phil

+0

Includes()는 더 단순한 쿼리를 생성 할 수 있지만 여전히 큰 쿼리가 될 것이며 @Phil이 지적한대로 결과 크기에서 누락 될 수있는 쿼리 단순성에서 얻을 수있는 것입니다. 사용자를 가져올 때 Include()를 사용하여 관련 엔터티의 한 지점을 가져올 수 있지만 모든 항목을 가져올 수는 없습니다. –