【问题标题】:How to improve EF query performance with 20+ Includes()如何使用 20 多个 Includes() 提高 EF 查询性能
【发布时间】:2013-06-07 05:23:02
【问题描述】:

我有一个Activity 模型,它从大约 7 种不同类型的模型中提取模型。这些模型也有它们的关系,活动提要需要能够显示我想要的信息。这意味着我的查询中有大约 20 个包含。我这样做才 8 个月左右,而且我已经阅读了有关编译查询、存储过程以及所有这些内容可能会杀死我的内容。我还读到我可以将我的代码优先模型更改为virtual,以便它可以进行延迟加载,但我担心如果我有大量用户,所有这些数据库调用都会杀死我的网站。

首先是模型

public class Activity
{
    public int ActivityID { get; set; }
    public int ActivityTypeID { get; set; }
    public int ContributorID { get; set; }
    public int UserID { get; set; }
    public int? ProjectID { get; set; }
    public int? ProjectDocID { get; set; }
    public int? CommentID { get; set; }
    public int? BookID { get; set; }
    public int? BookReviewID { get; set; }
    public DateTime DateCreated { get; set; }
    public Comment Comment { get; set; }
    public ProjectDoc ProjectDoc { get; set; }
    public Project Project { get; set; }
    public Book Book { get; set; }
    public BookReview BookReview { get; set; }
    public ActivityType ActivityType { get; set; }
    [ForeignKey("ContributorID")]
    public User Contributor { get; set; }
    [ForeignKey("UserID")]
    public User User { get; set; }
    public ICollection<ActivityLike> ActivityLike { get; set; }
    public ICollection<ActivityComment> ActivityComment { get; set; }
}

现在查询

 var activity = db.Activities
                .Include(i => i.Contributor.BookStatus)
                .Include(i => i.ActivityType)
                .Include(i => i.ActivityLike.Select(y => y.User))
                .Include(i => i.ActivityComment.Select(y => y.User))
                .Include(i => i.Project.ProjectFollower)
                .Include(i => i.Project.View)
                .Include(i => i.Project.ProjectType)
                .Include(i => i.Project.User)
                .Include(i => i.Project.ProjectTag.Select(v => v.Tag))
                .Include(i => i.Project.ProjectCategory.Select(v => v.Category))
                .Include(i => i.Project.ProjectCharacteristic.Select(v => v.Characteristic))
                .Include(i => i.Project.ProjectDoc.Select(v => v.ProjectDocVote))
                .Include(i => i.Project.ProjectDoc.Select(v => v.User))
                .Include(i => i.Comment.User)
                .Include(i => i.Book.Author)
                .Include(i => i.Book.BookReview.Select(v => v.User))
                .Include(i => i.Book.BookReview.Select(v => v.BookReviewVote))
                .Include(i => i.Book.BookCharacteristic.Select(v => v.Characteristic))
                .Include(i => i.Contributor.Followers)
                .Where(u =>
                    u.Contributor.Followers.FirstOrDefault(x => x.FollowerID == WebSecurity.CurrentUserId) != null
                )
                .OrderByDescending(d => d.DateCreated)
                .Skip(offset)
                .Take(results)
                .ToList();

这最终就像 6600 行 SQL(或其他任何内容)。

第一次运行需要 10-16 秒。而且因为我使用Skip() 和jquery 无限滚动,所以第一次每次调用ajax 需要10-12 秒。因此,如果我每次得到 10 个结果并且有 100 个结果,那么等待时间超过 100 秒,这太糟糕了。现在,下次用户在短时间内访问该页面时,速度非常快。

那么我应该如何改进此查询,您能否具体提供如何改进并解释解决方案,因为我很难理解已编译的查询以及其他问题。我能够做到这一点的唯一原因是因为 EF 让它变得简单……这显然是有代价的。

请原谅和滥用的术语。

【问题讨论】:

  • 你在使用 DbContext 吗?
  • 我相信是这样,即'private NameDBContext db = new NameDBContext();'
  • 在 SQL Server Management Studio 中触发此查询需要多长时间。使用查询探查器分析您的查询。 :)

标签: entity-framework linq-to-entities


【解决方案1】:

我的建议:

  1. 确保正确调整数据库索引。无论您如何生成查询 SQL,没有正确索引的连接都会影响您的性能。如果您使用的是 SQL Server,使用Database Engine Tuning Advisor 很容易做到这一点。对于初学者,一般规则是应该为外键列建立索引。
  2. 考虑用普通的旧 SQL 编写此关键路径查询。在您从事 Entity Framework 工作的短时间内,您已经掌握了很多关于 Entity Framework 的技巧,而且我确信可能有一种方法可以优化您所做的工作,但肯定是加快这一进程的有效方法up是绕过ORM层。

【讨论】:

  • 我首先用代码完成了所有数据建模,我相信这会设置索引,但我不知道。我在 Windows azure 上托管该站点,所以我认为它是 sql azure。一般来说,我不知道该怎么做。
  • 如果您使用的是 SQL Azure,您可以设置一个数据库副本以在本地 SQL 实例上运行并针对本地实例进行测试。这将允许您运行 SQL Profiler 并使用优化顾问分析结果。 EF 代码优先不会自动在外键上设置索引。
  • 数据库显示有实体框架生成的外键和索引?
  • 我的错:看起来 EF 代码首先会自动为外键添加索引。
【解决方案2】:

我选择切换到延迟加载,这将加载时间减少了 70%。我意识到这并不理想,因为有更多的数据库往返,但如果网站增长,那么我会担心它。不理想,但我最初更关心性能。所以总的来说我做了以下

在导航属性中添加了virtual。 删除了几乎所有超重查询的包含

结果是 2.5 秒的加载时间而不是 15 秒(基于浏览器时间线而不是 sql 分析器)。

【讨论】:

    【解决方案3】:

    在我的项目中,我创建了使用 EF 的存储库,所以我写如下

      public IQueryable<Company> Companies
        {
            get
            {
                return context.Companies.Include("BankAccounts").Include("CompanyContacts").Include("MyClients").Include("MyCompanies").Include("Address");
            }
        }
    

    现在我可以在不包含的情况下调用代码中的任何地方 Companies。

    CompanyRepository comrep = new CompanyRepository();
    var companies = comrep.Companies(c=>c.FullName == "Made in Azerbaijan").SingleOrDefault();
    

    希望对你有帮助

    【讨论】:

    • 当您调用 comrep.Companies 时,包含仍在执行中,因此这将一事无成。
    猜你喜欢
    • 2019-12-29
    • 2014-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-03
    • 2012-10-28
    • 2016-01-17
    相关资源
    最近更新 更多