在我的应用程序中,代码优先执行查询比常规EF慢3倍

本文关键字:查询 常规 EF 3倍 执行 应用程序 我的 代码 | 更新日期: 2023-09-27 18:10:09

我有一个喜欢的项目(一个简单的论坛应用程序),我用它来测试所有最新的。net技术,我最近开始玩实体框架代码优先。这个应用程序已经有一个现有的EF解决方案,一个EDMX文件映射到一个现有的数据库,我所有的实体都是自动生成的。到目前为止,这个解决方案非常有效。

注意:请记住,EF 4.1的这个变化纯粹是为了学习。如果你想知道我的需求是什么,导致我升级,没有任何。我只是为了好玩。

我复制了该项目并进行了升级,因此我将拥有相同的项目,但具有不同的实体框架实现。在新项目中,我使用了一个名为实体框架电源工具的Visual Studio扩展,从我现有的数据库中生成poco和DbContext。一切都完美无缺。我在30分钟内完成了这个应用程序的编译。非常令人印象深刻。

然而,我注意到现在运行应用程序时,查询执行速度比以前慢了大约3倍。你知道我错过了什么吗?

以下是两种解决方案的详细信息,以及两者的LINQPad测量结果。(点击图片查看完整尺寸)

EF 4.0 Details

这是我EF 4.0数据模型的快照。它切断了顶部和底部的一些实体,但是你明白了。

http://www.codetunnel.com/content/images/EF41question/1.jpg以下是针对我的EF 4.0数据模型的LINQPad测试。

http://www.codetunnel.com/content/images/EF41question/2.jpg注意,查询的执行时间为2.743秒。

EF 4.1 Details

这是我的EF 4.1数据模型的快照。因为它是纯代码的,所以我将展示DbContext类以及一个实体和一个实体本身的一个映射类(流畅的API代码)。

DbContexthttp://www.codetunnel.com/content/images/EF41question/3.jpgTopicMap(流畅API配置)http://www.codetunnel.com/content/images/EF41question/4.jpg主题(POCO实体)http://www.codetunnel.com/content/images/EF41question/5.jpg以下是对我的EF 4.1模型的LINQPad测试。

http://www.codetunnel.com/content/images/EF41question/6.jpg注意,这次查询的执行时间为6.287秒,而且是完全相同的查询。它第一次运行需要30多秒。如果我转到LINQPad中的SQL和IL选项卡,生成的SQL和IL代码对于两个数据模型是相同的。这真的让我很伤心。在实际应用中,EF 4.1的速度很慢,以至于无法使用。

我对两个模型运行了相同的LINQ查询。该查询获取一个普通论坛用户的所有主题,按最后一次回复日期(如果没有回复,则按主题发布日期)降序排列。

显然,我可以回到EF 4.0,继续我的快乐之路,但我真的很感兴趣,如果我可能错过了什么。

在我的应用程序中,代码优先执行查询比常规EF慢3倍

UPDATE

由于最近的一些发展,我正在重新考虑这个答案。

由于微软实体框架团队试图重复我的问题,所以我返回并追溯了我的步骤,以更好地帮助缩小问题范围。我问这个问题已经有一段时间了,我现在对事情的理解比那时好多了。

我决定从一个简单的测试项目开始,而不是回去尝试运行一些非常旧的代码。我把一个简单的数据库和两个表放在一起,并将它们映射到EF 4.0设计器文件。

生成如下连接字符串:

<add name="EFTestEntities" connectionString="metadata=res://*/Entities.csdl|res://*/Entities.ssdl|res://*/Entities.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.'sqlexpress;initial catalog=EFTest;integrated security=True;multipleactiveresultsets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />

然后用1000行测试主题和10行每个主题的回复填充数据库。一旦我有了这个工作,我计时一个非常基本的查询非常类似于我的主要问题。然后我复制了测试项目,并使用实体框架电源工具扩展对其进行了修改,以生成模型对象和DbContext。我唯一修改的是连接字符串,以删除在项目中有设计器文件时引用的元数据,因此它看起来像这样:

<add name="EFTestContext" providerName="System.Data.SqlClient" connectionString="Data Source=.'sqlexpress;Initial Catalog=EFTest;Integrated Security=True;Pooling=False" />

然后我运行了与设计器完全相同的查询。

在查询时间上没有区别,除了代码优先生成映射元数据所花费的时间稍微多了一点。在最初的查询之后,两个版本的EF执行得几乎相同。我本来打算把这个问题解决为不可复制的,但后来我注意到我在问题中做了一些可怕的事情。在我的查询之前,我打电话给.AsEnumerable()。如果你还不知道它的作用,这将导致整个实体集合被拉入内存,然后查询将在那里应用为linq到对象而不是linq到实体。

这意味着我将整个表吸到内存中,然后在那里对它执行LINQ。在SQL服务器与您的网站在同一台机器上的情况下,您可能不会注意到差异,但在许多情况下,这将是一个巨大的问题。在我的例子中,它确实造成了性能损失。

我回到我的测试,我用.AsEnumerable()放在查询之前运行它们。

现在我预计时间会慢一些,因为我的LINQ查询没有被翻译成表达式树并在数据库中执行。然而,似乎我确实在我的问题中重现了这个问题。纯代码版本的返回速度要慢得多。这实际上很奇怪,因为它们应该运行相同。我并不惊讶它们比IQueryable查询时运行得慢,但是现在它们在IEnumerable上运行,两者之间有很大的区别。通过向表中添加越来越多的数据,我能够扩大两者之间的差异。

我继续在数据库中添加了5000多个主题,每个主题有30个回复。所以现在总共有6000个主题行和165000个回复行。首先,我使用适当的LINQ-to-Entities运行查询:

如你所见,仍然没有区别。然后,我使用.AsEnumerable()对LINQ-to-Objects运行查询。

我在三个查询后停止了它,因为每个查询等待大约两分钟是非常痛苦的。我似乎不能产生3倍慢的问题,我在我的问题中显示,但代码是明显慢。EDMX方法只需不到两分钟就能完成一个查询,而纯代码方法始终需要两分钟以上的时间。

如果我没有误解,你有两种情况,在这两种情况下,你比较查询的性能:

  1. 在您的问题中,您将EF 4.0中使用ObjectContext API的查询性能与EF 4.1中使用Code-FirstDbContext API的相同查询性能进行比较。现在,如果我在EF 4.0方法的代码库中看到正确的话,您使用的是EntityObject派生实体,而不是POCO(从EF 4.0的T4 POCO生成器生成),而在EF 4.1解决方案中,您使用的是POCO。

    我的假设是这造成了差异。如果您使用EntityObject派生实体,您的对象能够跟踪自己的更改。另一方面,如果您使用poco, EF将在上下文中创建每个物化实体的属性快照,这对于EntityObjects来说是不必要的。创建快照可能会花费很长时间。

    所以这并不是EF 4.0和EF 4.1之间的比较,而是POCO和非POCO方法之间的比较。在实体框架中使用poco(未为更改跟踪代理做好准备,即每个属性都是virtual)在各个方面都较慢。

  2. 现在,你在更新中提到的你自己的答案很有趣。如果我理解正确,你在这里比较EF 4.1与DbContext使用Code-First (OnModelCreating具有实体配置,连接字符串没有对EDMX文件的引用)和EF 4.1与DbContext使用Database-First (OnModelCreating为空(或仅从DbContext生成器抛出此UnintentionalCodeFirstException)连接字符串有对EDMX文件的引用)。

    在这两种情况下,您使用相同的POCO实体。这里的性能差异让我感到惊讶,我没有一个直接的假设为什么会发生这种情况。但你没有提供确切的测量值(就像你问题中的情况一样),这会很有趣。

    对于使用EDMX方法的第一个查询更快,我并不感到惊讶,因为EF只需要读取和处理XML文件以在内存中构建模型,而没有EDMX,它必须反映程序集中的代码,这可能会更慢。但是,一旦模型在内存中构建(所以从最新的第二个查询开始),我的理解是EF将只使用模型的内存表示,而不管元数据的来源是什么。如果你在这种情况下真的有很大的性能差异,我就难倒了。

编辑

总结评论:我们正在谈论上面的第2点,这意味着你比较EF 4.1 Database-First (EDMX在连接字符串中指定,OnModelCreating为空)和EF 4.1 Code-First (EDMX未在连接字符串中指定,OnModelCreating包含模型配置)。结果:

使用EF 4.1 Database-First的查询速度是EF 4.1 Code-First的三倍。

对于一个简单的模型,我不能重现这种行为。我用Code-First创建了一个,并在User表中使用虚拟数据(300000用户("Spock 1"到"Spock 300000")测试了查询时间,Contains("pock") -查询用户名,因此所有300000用户必须返回并物化,需要3.2秒)。然后,我从代码优先模型生成了一个EDMX:

using (var context = new MyEntities())
{
    using (var writer = new XmlTextWriter("model.edmx", Encoding.Default))
    {
        EdmxWriter.WriteEdmx(context, writer);
    }
}
我将生成的EDMX文件添加到项目中,修改连接字符串以包含EDMX元数据,并再次运行查询。查询时间几乎完全相同(3.2秒)。

所以,问题的原因显然不那么明显。这可能与模型或查询的复杂性有关。