EntityFramework:使用~11000记录在IQueryable上调用ToList()需要10秒

Dej*_*vić 2 linq entity-framework sql-server-2008-r2 entity-framework-4 tolist

我想从SQL Express 2008 R2服务器返回相对大量的记录,通过EntityFramework 4到WCF服务到WCF客户端.我的测试表目前包含大约11.000条记录.LINQ查询就像这样简单:

Database DB = new Database(); // create object context
var retValue = DB.Entities.Persons
        .Include("District")
        .Include("District.City")
        .Include("District.City.State")
        .Include("Nationality")

return retValue.ToList();
Run Code Online (Sandbox Code Playgroud)

这大约需要10秒钟才能完成.

在SQL Server Managament Studio中执行时,相同的SELECT查询所需的时间不到1秒.

在EF中它是否必须缓慢?

Sla*_*uma 5

您的查询并不简单,它包含大量连接(由于Includes),更重要的是它可能会返回大量重复数据,特别是如果包含的导航属性是集合:https://stackoverflow.com/a/5522195/270591

时间消耗部分是对象实现,并且当数据库的结果返回到实体框架上下文时将实体附加到上下文.

这可以通过您的测量(在您的问题的评论中)确认在同一上下文中的第二个查询非常快.在这种情况下,EF将对数据库执行查询,但不需要再次实现对象,因为它们仍然附加到上下文.

如果在第二个上下文中运行第二个查询,则生成的实体必须附加到新上下文 - 此步骤再次变慢(也由您的测量确认).

这可能是一个点,与EF查询事实上慢,增加了大量的开销相比原始的SQL查询.EF需要创建许多数据结构,为变更跟踪和管理上下文中的对象标识做好准备,这会花费额外的时间.

我能看到改善性能的唯一方法是禁用更改跟踪(假设您不需要它来进行操作).在EF 4.0/ObjectContext它将是:

Database DB = new Database();
DB.Entities.Persons.MergeOption = MergeOption.NoTracking;
// MergeOption is in System.Data.Objects namespace
Run Code Online (Sandbox Code Playgroud)

使用这种方法时,必须要注意的是,相关对象将被创建为单独的对象,即使它们具有相同的密钥 - 这与启用的更改跟踪不同,因为附加到上下文将避免这种重复.

因此,可能会有更多对象加载到内存中.如果这会适得其反,实际上性能下降甚至更多,或者它仍然表现更好是一个测试问题.