实体框架查询太慢了

时间:2018-01-07 11:41:16

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

我必须在您的数据库上放置一个复杂的查询。但查询结束时间为8000毫秒。我做错了吗?我使用.net 1.1和Entity Framework核心1.1.2版本。

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();

2 个答案:

答案 0 :(得分:2)

查看您为服务器生成的SQL查询(以及此查询的结果)。对于SQL Server,最好的选择是SQL Server Profiler,也有其他服务器的方法。

  • 您创建了两个查询。首先创建fol数组,然后使用Contains将其传递给第二个查询。你知道这是怎么回事吗?您可能使用与数组中的许多项一样多的参数生成查询。它既不漂亮也不高效。这里没有必要,将它合并到主查询中,你只有一个参数。

  • 你在过滤之前做分页,这真的应该是这样吗?还要看一下基于id过滤的其他分页方式,而不是简单的跳过。

  • 您在一个查询中执行了太多的旁边查询。当您查询每个包含100个项目的三个子列表时,您不会获得300行。要在一个查询中获取它,您创建连接并实际获得100 * 100 * 100 = 1000000行。除非您确定框架可以将其拆分为多个查询(可能不能),否则您应该在单独的查询中查询子列表。这可能是您遇到的主要性能问题。

  • 请使用单数来命名表格,而不是复数

  • 对于性能分析,索引结构和执行计划是至关重要的信息,没有它们你就不能说太多了

答案 1 :(得分:1)

如评论中所述,您可能正在执行100,1000或10000次查询。对于数据库中与第一个结果匹配的每个Vote,您执行3个其他查询。

对于第一个查询产生的1000票,您需要执行3000个其他查询来获取数据。那太疯了!

您必须使用EF Cores eager loading功能,只需很少的查询即可获取此数据。如果您的模型设计得很好,relations and navigation properties很容易。

当您在没有投影的情况下加载平面模型时(使用.Select),您必须使用.Include来告知EF应加载哪些其他相关实体。

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

这将通过投票加载所有VoteMedia个对象。因此不需要额外的查询来获取它们。

但是如果您使用项目,则.Include调用不是必需的(实际上,当您在投影中引用导航属性时,它们甚至会被忽略)。

// 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 => ... );
    }