首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将EF4机型升级到EF5机型(DbContext)后性能下降

将EF4机型升级到EF5机型(DbContext)后性能下降
EN

Stack Overflow用户
提问于 2012-11-20 23:08:04
回答 1查看 458关注 0票数 5

我在升级到.NET 4.5和EF5 (引用新的EntityFramework 5程序集)的.NET 4.0应用程序中有一个EF4模型,我将“代码生成策略”更改为“无”,并向该模型添加了一个代码生成项(EF 5.x DbContext生成器)。几乎在任何情况下都能很好地工作。但是现在,当我访问一个引用了很多记录(> 100.000条记录)的导航属性时,遇到了很大的问题。数据库是MSSQL2005Server。

我的场景是这样的:

我的数据库中的每个客户都有一个唯一的ID (它是数据库中的主键),此外,每个客户记录都包含一个父客户ID (在这种特殊情况下,几乎每个客户都引用相同的父id (大约150.000条记录中的145.000条记录),该记录的id为1)。

我的模型包含DbSet<CustomerBase> CustomerBase,它表示包含所有客户及其数据的表。此外,还有名为ICollection<CustomerBase> CustomerBaseChildrenICollection<CustomerBase> CustomerBaseParent的导航属性,它们使用0..1到*的重数连接客户id和客户父id。

我构建了一个简化的版本来演示我的意思:

为此测试构建包含150.000条记录的表:

代码语言:javascript
复制
CREATE TABLE CustomerBase
(
  id int IDENTITY(1,1) PRIMARY KEY NOT NULL,
  parent_id int FOREIGN KEY REFERENCES CustomerBase(id),
  some_data1 varchar(100),
  some_data2 varchar(100),
  some_data3 varchar(100),
  some_data4 varchar(100),
  some_data5 varchar(100),
)
GO

DECLARE @i int = 0
WHILE @i < 150000 BEGIN
  INSERT INTO CustomerBase (parent_id, some_data1, some_data2, some_data3, some_data4, some_data5) VALUES (1, newid(), newid(), newid(), newid(), newid())

  SET @i = @i + 1
END

将包含引用约束的表导入到新的实体模型中。我使用ef5Entities作为“实体容器名称”。然后,我将导航属性CustomerBase1和CustomerBase2重命名为CustomerBaseChildren和CustomerBaseParent。

下面是我的示例应用程序:

代码语言:javascript
复制
static void Main(string[] args)
{
  ef5Entities context = new ef5Entities();

  // Start with selecting a single customer.
  // Works fine in EF4/ObjectContext, works even faster in in EF5/DbContext
  CustomerBase someCustomer = context.CustomerBase.First(customer => customer.id == 1234);
  // Do something ...

  // Get the parent of the customer.
  // Works fine in EF4/ObjectContext, works even faster in in EF5/DbContext
  CustomerBase parentCustomer = someCustomer.CustomerBaseParent;
  // Do something ...

  // Get the first child of the given parent id.
  // Takes about 10 seconds in EF4/ObjectContext, I stopped the debugger after 2 minutes waiting in EF5/DbContext
  CustomerBase firstChild = parentCustomer.CustomerBaseChildren.First();

  Console.WriteLine("Press any key to quit.");
  Console.ReadKey();
}

我使用SQL Server事件探查器查看在数据库上执行的实体框架。看起来EF4和EF5代码是完全相同的:

代码语言:javascript
复制
SELECT TOP (1) 
[Extent1].[id] AS [id], 
[Extent1].[parent_id] AS [parent_id], 
[Extent1].[some_data1] AS [some_data1], 
[Extent1].[some_data2] AS [some_data2], 
[Extent1].[some_data3] AS [some_data3], 
[Extent1].[some_data4] AS [some_data4], 
[Extent1].[some_data5] AS [some_data5]
FROM [dbo].[CustomerBase] AS [Extent1]
WHERE 1234 = [Extent1].[id]

exec sp_executesql N'SELECT 
[Extent1].[id] AS [id], 
[Extent1].[parent_id] AS [parent_id], 
[Extent1].[some_data1] AS [some_data1], 
[Extent1].[some_data2] AS [some_data2], 
[Extent1].[some_data3] AS [some_data3], 
[Extent1].[some_data4] AS [some_data4], 
[Extent1].[some_data5] AS [some_data5]
FROM [dbo].[CustomerBase] AS [Extent1]
WHERE [Extent1].[id] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

exec sp_executesql N'SELECT 
[Extent1].[id] AS [id], 
[Extent1].[parent_id] AS [parent_id], 
[Extent1].[some_data1] AS [some_data1], 
[Extent1].[some_data2] AS [some_data2], 
[Extent1].[some_data3] AS [some_data3], 
[Extent1].[some_data4] AS [some_data4], 
[Extent1].[some_data5] AS [some_data5]
FROM [dbo].[CustomerBase] AS [Extent1]
WHERE [Extent1].[parent_id] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

如果我在SQL Management Studio中执行所有这三条语句,则需要大约1-2秒时间才能获取所有1+1+ 150.000条记录。

但据我所知,第三个陈述是问题所在。它返回150.000条记录(即使我像上面的代码一样使用.First(),或者使用.Single().Take(10),不管我是否在前面使用.OrderBy(...)。看起来实体框架获取了所有的150.000条记录,在DbContext中缓存这些记录需要花费大量的时间(在等待了2分钟之后,我停止了测试代码,用我的真实客户基表测试它花了100分钟才完成)。在ObjectContext中缓存只需要大约10秒(考虑到数据库本身要快5-10倍,这是很糟糕的,但我可以接受它)。

即使是内存消耗也是可怕的,使用ObjectContext时,应用程序工作集增加了大约200mb,使用DbContext时,应用程序工作集增加了大约10倍。

如果我只想要第一条记录或前n条记录(通常是10到100条记录),有没有办法在select语句中插入一个TOP(n)子句来停止从数据库接收所有记录?在第一个语句中,select语句中有一个TOP(1) (如果使用.Single()而不是.First(),则有一个TOP(2) )。

我甚至尝试将行CustomerBase someCustomer = context.CustomerBase.First(customer => customer.id == 1234);更改为no-tracking:CustomerBase someCustomer = context.CustomerBase.AsNoTracking().First(customer => customer.id == 1234);

但随后在CustomerBase firstChild = parentCustomer.CustomerBaseChildren.First();上获取了一个System.InvalidOperationException,并显示了以下消息:

当使用NoTracking合并选项返回对象时,仅当EntityCollection或EntityReference不包含对象时才能调用Load。

如果我将代码生成策略改回使用ObjectContext和EF5,一切都会像以前的EF4一样正常工作。是我在使用DbContext时做错了什么,还是DbContext在更大的环境中不能使用?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-12-05 00:51:05

我最近升级到了Visual Studio2013,.NET 4.5.1和Entity Framework6。如果我修改我的模型,使用EF6而不是EF5,它就像一个护身符。

因此,解决方案是将EF4/EF5与ObjectContext或EF6与DbContext一起使用。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13476088

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档