EF 4.1 Code-first在我的应用程序中执行比常规EF慢3倍的查询

时间:2011-09-29 23:46:20

标签: c# .net entity-framework entity-framework-4 entity-framework-4.1

我有一个宠物项目(一个简单的论坛应用程序),我用它来测试所有最新的.NET技术,我最近开始使用Entity Framework Code-First。这个应用程序已经有一个现有的EF解决方案,EDMX文件映射到现有数据库,我的所有实体都是自动生成的。到目前为止,这个解决方案运作良好。

注意:请记住,对EF 4.1的此更改纯粹是为了学习。如果你想知道我的需求是什么导致我升级,那就没有了。我只是想这么开心。

我复制了项目并进行了升级,因此我将拥有相同的项目但具有不同的实体框架实现。在新项目中,我使用名为Entity Framework Power Tools的Visual Studio扩展来从现有数据库生成POCO和DbContext。一切都完美无瑕。我在大约30分钟的时间内编译了应用程序。非常令人印象深刻。

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

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

EF 4.0详细信息

以下是我的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详细信息

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

的DbContext http://www.codetunnel.com/content/images/EF41question/3.jpg TopicMap(流畅的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并开始我的快乐方式但我真的很感兴趣,如果可能有我遗漏的东西。

2 个答案:

答案 0 :(得分:5)

更新

由于最近的一些发展,我完全重新回答了这个答案。

由于Microsoft的实体框架团队试图复制我的问题,我回过头来回顾我的步骤以更好地帮助缩小问题范围。自从我提出这个问题以来已经有一段时间了,我现在对事情的了解比我当时要好得多。

我没有回去试图让一些非常旧的代码运行,而是决定从头开始使用一个简单的测试项目。我将一个带有两个表的简单数据库放在一起,并将它们映射到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行回复。一旦我完成了这项工作,我就会得到一个非常基本的查询,与我主要问题中的查询非常类似。然后我复制了测试项目,并使用Entity Framework Power Tools扩展名对其进行了修改,以生成模型对象和DbContext。我修改的唯一内容是连接字符串,用于删除项目中存在设计器文件时引用的元数据,因此它看起来像这样:

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

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

查询时间没有差异,除了代码优先生成映射元数据所需的额外时间。在初始查询之后,两个版本的EF执行几乎相同。我即将解决这个问题,因为这个问题不可重复,但后来我注意到我在这个问题上做了一些可怕的事情。我在查询之前打电话给.AsEnumerable()。如果你还不知道那是什么,那将导致ENTIRE实体集合被拉入内存,然后查询将作为LINQ-to-Objects而不是LINQ-to-Entities应用于那里。

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

我回到了测试阶段,并在查询前放置.AsEnumerable()来运行它们。

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

我继续向数据库添加了5000多个主题,每个主题有30个回复。因此,现在总共有6000个主题行和165000个回复行。首先,我使用适当的LINQ到实体运行查询:

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

我在三次查询后停止了它,因为每次查询等待大约两分钟是令人难以忍受的。我似乎无法生成我在问题中显示的3x缓慢问题,但仅代码显着慢。 EDMX方法只需要两分钟即可完成一个查询,而仅使用代码的方法则需要两分钟。

答案 1 :(得分:1)

如果我没有误解你有两种情况,在这两种情况下你都会比较查询的表现:

  1. 在您的问题中,您将使用 Code-First {1}} API将查询的性能与EF 4.0中的ObjectContext API与使用DbContext API的相同查询的性能进行比较em>在EF 4.1中。现在,如果我在您的代码存储库中看到EF 4.0方法,您已经使用EntityObject派生实体而不是POCO(从EF4的T4 POCO生成器生成),而在EF 4.1解决方案中,您有POCO。

    我的假设是,这有所不同。如果您使用的是EntityObject派生实体,则您的对象可以跟踪自己的更改。另一方面,如果您使用POCO,EF将在上下文中创建每个物化实体的属性快照,这对EntityObjects不是必需的。创建快照可能需要很长时间。

    因此,这不是EF 4.0和EF 4.1之间的比较,而是POCO和非POCO方法之间的比较。 (在实体框架中使用POCO(未准备好更改跟踪代理,即每个属性为virtual)在每个方面都较慢。如果性能很重要,则必须避免它们。)

  2. 现在,你在UPDATE中提到你自己的答案很有意思。如果我理解正确,那么使用代码优先DbContext具有实体配置且连接字符串没有对EDMX文件的引用)将EF 4.1与OnModelCreating进行比较使用 Database-First DbContext的{​​{1}}的EF 4.1为空(或仅从DbContext生成器抛出此OnModelCreating)并且连接字符串具有对EDMX文件)。

    在这两种情况下,您使用的是相同的POCO实体。在这里,性能差异对我来说是令人惊讶的,我没有立即假设为什么会发生这种情况。您没有提供准确的测量结果(如您问题中的情况),这将是有趣的。

    我对EDMX方法的第一个查询速度更快并不感到惊讶,因为EF只需要读取和处理XML文件以在内存中构建模型,而没有EDMX它必须反映组件中的代码慢点。但是一旦模型建立在内存中(所以从最新的第二个查询开始),我的理解是EF只能使用模型的内存表示,无论元数据的来源是什么。如果你在那种情况下确实有很大的表现差异,我很难过。

  3. 修改

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

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

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

    Contains("pock")

    我添加到项目中的结果EDMX文件,修改了连接字符串以包含EDMX元数据,然后再次运行查询。查询时间几乎完全相同(3.2秒)。

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