处理急切加载包含语句时实体框架缓慢的性能

本文关键字:框架 实体 缓慢 性能 语句 加载 包含 处理 | 更新日期: 2023-09-27 18:02:10

我试图使用实体框架在摘要网页上显示SQL Server数据库中的大量关系,我发现在查询中使用许多include语句的性能非常糟糕。

要求一次在一个页面上显示单个用户的所有数据,通常,这不是大量的数据,但是获取它确实需要遍历相当多的EF关系,例如下面的查询

var class = context.Class.Where(a => a.Id.Equals(Id))
                      .Include(a => a.Teacher.Address)
                      .Include(a => a.Teacher.Supplies.Notebooks)
                      .Include(a => a.Teacher.Supplies.Pencils)
                      .Include(a => a.Teacher.Supplies.Textbooks)
                      .Include(a => a.Teacher.Supplies.Erasers)
                      .Include(a => a.Students.Select(d => d.Supplies.Notebooks))
                      .Include(a => a.Students.Select(d => d.Supplies.Pencils))
                      .Include(a => a.Students.Select(d => d.Supplies.Textbooks))
                      .Include(a => a.Students.Select(d => d.Supplies.Erasers))
                      .Include(a => a.Configuration)
                      .Include(a => a.Payment.Payer.Address)
                      .Include(a => a.Payment.PaymentMethod)
                      .First();

在包含最少数据的测试数据库上运行需要超过10秒的时间。但是,如果我这样做,性能将花费约1秒:

var class = context.Class.Where(a => a.Id.Equals(Id)).Include(a => a.Teacher.Address).First();
    class = context.Class.Where(a => a.Id.Equals(Id)).Include(a => a.Teacher.Supplies.Notebooks).First();
    class = context.Class.Where(a => a.Id.Equals(Id)).Include(a => a.Teacher.Supplies.Pencils).First();
    class = context.Class.Where(a => a.Id.Equals(Id)).Include(a => a.Teacher.Supplies.Textbooks).First();
    class = context.Class.Where(a => a.Id.Equals(Id)).Include(a => a.Teacher.Supplies.Erasers).First();
    class = context.Class.Where(a => a.Id.Equals(Id)).Include(a => a.Students.Select(d => d.Supplies.Notebooks)).First();
    class = context.Class.Where(a => a.Id.Equals(Id)).Include(a => a.Students.Select(d => d.Supplies.Pencils)).First();
    class = context.Class.Where(a => a.Id.Equals(Id)).Include(a => a.Students.Select(d => d.Supplies.Textbooks)).First();
    class = context.Class.Where(a => a.Id.Equals(Id)).Include(a => a.Students.Select(d => d.Supplies.Erasers)).First();
    class = context.Class.Where(a => a.Id.Equals(Id)).Include(a => a.Configuration).First();
    class = context.Class.Where(a => a.Id.Equals(Id)).Include(a => a.Payment.Payer.Address).First();
    class = context.Class.Where(a => a.Id.Equals(Id)).Include(a => a.Payment.PaymentMethod).First();

这真的是最好的方式来运行查询来获取所有这些数据,还是我这样做完全错误?

处理急切加载包含语句时实体框架缓慢的性能

这里有18个连接,所以无论如何都不会很快。然而,根据您测试的方式,您的结果可能很重要,也可能不重要。特别是,如果您正在调试或只是在本地运行,一般情况下,一切都会变慢。如果你使用LocalDb,它会比SQL Server慢。IIS Express是单线程的,而IIS是多线程的,所以这对性能也有影响。

首先,也是最重要的,你应该让像Glimpse这样的东西在你的项目中运行。这将使您实际地将加载页面所需的时间与运行查询所需的时间分开,并使您能够看到正在运行的查询的数量和范围。但是,直到您在实际的类似生产的机器上测试,使用完整的IIS和SQL Server,您才会真正知道这将如何执行。

如果这是一个真正的问题,您可以考虑创建一个存储过程来返回所有这些信息。这将比EF所能做的任何事情都要快得多,因为SQL Server可以存储执行计划并优化查询。如果您发现实体框架对于您的目的来说太慢了,通常,您还可以研究使用替代orm,如Dapper。当然,你会有一个学习曲线,但如果你的主要关注点是性能,你几乎总是可以比实体框架更好,尽管你可能会在这个过程中失去一些细节。

不幸的是,我无法解释为什么Include技术如此缓慢,但这就是我试图获取数据的方式:

var class = context
    .Class
    .Where(a => a.Id.Equals(Id))
    .First()
.Select(a => new 
{
    TA = a.Teacher.Address,
    TN = a.Teacher.Supplies.Notebooks,
    TP =a.Teacher.Supplies.Pencils,
    TT = a.Teacher.Supplies.Textbooks,
    TE = a.Teacher.Supplies.Erasers,
    SN = a.Students.Select(d => d.Supplies.Notebooks),
    SP = a.Students.Select(d => d.Supplies.Pencils),
    ST = a.Students.Select(d => d.Supplies.Textbooks),
    SE = a.Students.Select(d => d.Supplies.Erasers),
    a.Configuration,
    PA = a.Payment.Payer.Address,
    a.Payment.PaymentMethod
};

这会提高性能吗?如果是的话,我会对两种技术生成的sql之间的差异感兴趣