2012-11-20 41 views
5

我在.NET 4.0應用程序中有一個EF4模型,我升級到.NET 4.5和EF5(引用新的EntityFramework 5程序集),我更改了「代碼生成策略「更改爲」無「,並向模型添加了代碼生成項目(EF 5.x DbContext生成器)。幾乎在任何情況下都可以正常工作。但是現在當我訪問一個引用大量記錄(> 100.000條記錄)的導航屬性時會遇到很大的問題。該數據庫是MSSQL 2005服務器。將EF4模型升級爲EF5模型(DbContext)後性能下降

我的情況是這樣的:

在我的分貝每一個客戶都有一個唯一的ID(這是在數據庫中的主鍵),除了每一個客戶記錄包含父客戶ID(在這種特殊情況下,幾乎所有的客戶對同一父母id的引用(150.000條記錄中約有145.000條記錄),這是記錄ID爲1的)。

我的模型包含DbSet<CustomerBase> CustomerBase,它代表包含所有客戶數據的表。此外還有導航屬性,稱爲ICollection<CustomerBase> CustomerBaseChildrenICollection<CustomerBase> CustomerBaseParent,它們將客戶ID和客戶父ID以0..1到*的多重性連接起來。

我建立一個簡化版本,來證明我的意思:

構建表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。然後,我將Navigation Adierties 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 Profiler來查看數據庫上執行什麼樣的實體框架。看來,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(...)似乎Entity Framework獲取所有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);更改爲無跟蹤:CustomerBase someCustomer = context.CustomerBase.AsNoTracking().First(customer => customer.id == 1234);

但接下來在CustomerBase firstChild = parentCustomer.CustomerBaseChildren.First();以下消息得到System.InvalidOperationException

當一個對象被用NoTracking合併選項返回,僅當EntityCollection或EntityReference不包含對象時才能調用加載。

如果我將代碼生成策略更改爲使用ObjectContext和EF5,那麼在EF4中一切正常。我在使用DbContext的時候做錯了什麼,或者DbContext是在大環境下無法使用的嗎?

回答

1

我最近升級到Visual Studio 2013,.NET 4.5.1和實體框架6.如果我修改我的模型使用EF6而不是EF5,它就像一個魅力。

所以解決方法是使用EF4/EF5與ObjectContext或EF6與DbContext。

相關問題