简体   繁体   中英

Entity Framework Query is too slow

I have to put a complex query on your database. But the query ends at 8000 ms. Do I do something wrong? I use .net 1.1 and Entity Framework core 1.1.2 version.

var fol = _context.UserRelations
                  .Where(u => u.FollowerId == id && u.State == true)
                  .Select(p => p.FollowingId)
                  .ToArray();

var Votes = await _context.Votes
                          .OrderByDescending(c => c.CreationDate)
                          .Skip(pageSize * pageIndex)
                          .Take(pageSize)
                          .Where(fo => fol.Contains(fo.UserId))
                          .Select(vote => new
                {
                    Id = vote.Id,
                    VoteQuestions = vote.VoteQuestions,
                    VoteImages = _context.VoteMedias.Where(m => m.VoteId == vote.Id)
                        .Select(k => k.MediaUrl.ToString()),

                    Options =  _context.VoteOptions.Where(m => m.VoteId == vote.Id).Select( ques => new
                    {
                        OptionsID = ques.Id,
                        OptionsName =  ques.VoteOption,
                        OptionsCount =  ques.VoteRating.Count(cout => cout.VoteOptionsId == ques.Id),
                    }),
                    User = _context.Users.Where(u => u.Id == vote.UserId).Select(usr => new
                    {
                        Id = usr.Id,
                        Name = usr.UserProperties.Where(o => o.UserId == vote.UserId).Select(l => l.Name.ToString())
                            .First(),
                        Surname = usr.UserProperties.Where(o => o.UserId == vote.UserId)
                            .Select(l => l.SurName.ToString()).First(),
                        ProfileImage = usr.UserProfileImages.Where(h => h.UserId == vote.UserId && h.State == true)
                            .Select(n => n.ImageUrl.ToString()).First()
                    }),
                    NextPage = nextPage
                }).ToListAsync();

Have a look at the SQL queries you generate to the server (and results of this queries). For SQL Server the best option is SQL Server Profiler, there are ways for other servers too.

  • you create two queries. First creates fol array and then you pass it into the second query using Contains. Do you know how this works? You probably generate query with as many parameters as many items you have in the array. It is neither pretty or efficient. It is not necessary here, merge it into the main query and you would have only one parameter.

  • you do paginating before filtering, is this really the way it should work? Also have a look at other ways of paginating based on filtering by ids rather than simple skipping.

  • you do too much side queries in one query. When you query three sublists of 100 items each, you do not get 300 rows. To get it in one query you create join and get actually 100*100*100 = 1000000 rows. Unless you are sure the frameworks can split it into multiple queries (probably can not), you should query the sublists in separate queries. This would be probably the main performance problem you have.

  • please use singular to name tables, not plural

  • for performance analysis, indexes structure and execution plan are vital information and you can not really say much without them

As noted in the comments, you are potentially executing 100, 1000 or 10000 queries. For every Vote in your database that matches the first result you do 3 other queries.

For 1000 votes which result from the first query you need to do 3000 other queries to fetch the data. That's insane!

You have to use EF Cores eager loading feature to fetch this data with very few queries. If your models are designed well with relations and navigation properties its easy.

When you load flat models without a projection (using .Select ), you have to use .Include to tell EF Which other related entities it should load.

// Assuming your navigation property is called VoteMedia
await _context.Votes.
    .Include(vote => vote.VoteMedia)
    ...

This would load all VoteMedia objects with the vote. So extra query to get them is not necessary.

But if you use projects, the .Include calls are not necessary (in fact they are even ignored, when you reference navigation properties in the projection).

// Assuming your navigation property is called VoteMedia
await _context.Votes.
    .Include(vote => vote.VoteMedia)
    ...
    .Select( vote => new
    {
        Id = vote.Id,
        VoteQuestions = vote.VoteQuestions,

        // here you reference to VoteMedia from your Model
        // EF Core recognize that and will load VoteMedia too.
        //
        // When using _context.VoteMedias.Where(...), EF won't do that
        // because you directly call into the context
        VoteImages = vote.VoteMedias.Where(m => m.VoteId == vote.Id)
            .Select(k => k.MediaUrl.ToString()),

        // Same here
        Options = vote.VoteOptions.Where(m => m.VoteId == vote.Id).Select( ques => ... );
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM