我在升级到.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> CustomerBaseChildren和ICollection<CustomerBase> CustomerBaseParent的导航属性,它们使用0..1到*的重数连接客户id和客户父id。
我构建了一个简化的版本来演示我的意思:
为此测试构建包含150.000条记录的表:
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。
下面是我的示例应用程序:
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代码是完全相同的:
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在更大的环境中不能使用?
发布于 2013-12-05 00:51:05
我最近升级到了Visual Studio2013,.NET 4.5.1和Entity Framework6。如果我修改我的模型,使用EF6而不是EF5,它就像一个护身符。
因此,解决方案是将EF4/EF5与ObjectContext或EF6与DbContext一起使用。
https://stackoverflow.com/questions/13476088
复制相似问题