2011-07-09 56 views
1

我使用實體框架將特定對象的多個實例插入數據庫。我有一個單獨的對象上下文,我附上了幾個'產品'對象。我插入的對象與數據庫中的任何其他表都沒有關係。它純粹是一個獨立的實體。我正在使用'EFProf'工具來分析應用程序。實體框架批量插入導致選擇n + 1問題

當我調用'SaveChanges()'將我的'產品'實體持久化到SQL Server時,EFProf會警告我有'選擇N + 1'反模式。我不明白這是如何可能的,因爲我只是插入。我對'選擇N + 1'的理解是它涉及低效的對象檢索。我沒有檢索任何東西,只是插入。

當我檢查生成的SQL時,我發現Entity Framework已經爲我生成了一個select語句,它返回新插入對象的Id。這個select語句是爲我插入的每個實體執行的。這可能是選擇N + 1問題的原因嗎?如果是這樣,在SaveChanges()的單個調用中插入多個相同類型的實體時,如何避免這種反模式?

生成的SQL是如下:

insert [dbo].[Products] 
    ([ProductName], 
    [ProductNum], 
    [Price], 
    [EntryDate], 
    [Description], 
    [Category], 
    [UnitsInStock]) 
values('TestProduct' /* @0 */, 
    0 /* @1 */, 
    0 /* @2 */, 
    '2011-07-09T17:14:49.00' /* @3 */, 
    'Category: Test Products - Name TestProduct' /* @4 */, 
    'Test Products' /* @5 */, 
    0 /* @6 */) 


select [Id] 
from [dbo].[Products] 
where @@ROWCOUNT > 0 
     and [Id] = scope_identity() 

回答

1

不能避免額外的選擇。一旦您在數據庫中使用自動生成的ID並且您的模型已爲其正確配置,EF將始終創建此附加選擇。

你不需要打擾。使用EF進行「批量插入」的性能非常糟糕,以至於在每次插入期間進行額外的選擇都意味着什麼。您應該處理的是處理本身,因爲EF將爲每個插入的記錄創建單獨的數據庫往返,這就是問題所在。如果你真的想將大量的對象傳遞到數據庫中,你希望經常這樣做(這不是一次性工作),並且你期望一些性能,你應該尋找其他非EF解決方案。例如已經提到SqlBulkCopy

如果您只是需要在某些操作中插入少量實例,那麼您只需要。 EF就是這樣工作的。

+0

這是一個非常有用的評論謝謝。我正在做一些NHibernate和Entity Framework的性能測試。這是我運行的第一個測試用例,我的NHibernate結果非常快(即使沒有批處理),我開始想知道我的代碼有什麼問題。就我所見,我的測試用例實現了相同的功能。當你說EF性能對於批量插入來說「非常糟糕」時,你是以什麼爲基礎的?我需要寫出我的結果,所以如果您有其他的研究或文章描述這個問題將非常有用。謝謝。 – JMc

+0

這很容易。假設您在應用程序中創建了一些包含相關對象的對象。例如訂單與50個訂單項目。如果你有一個稱爲命令批處理的特性(如果使用正確的話,NHibernate也有這個功能),你將一次往返數據庫,它會包含51個插入(1個命令,50個項目)。如果您對EF執行相同的操作,它將按順序對數據庫執行51次往返操作,每次都包含一次插入。往返是幾乎所有可以做的操作中最慢的部分。 –

0

如果你正在嘗試做一個批量插入,在我看來,SCOPE_IDENTITY()是無用的,因爲它存儲一個標量值 - 而不是一個範圍。我要背叛我對EF的無知,但它是否將此代碼轉換爲BULK INSERT命令或SQLBulkCopy或其他?

如果你想在同一時間做一個插入,然後檢查@@ROWCOUNT第一,而不是把在where子句中似乎是在一般一個更好的模式:

IF @@ROWCOUNT = 1 
BEGIN 
    SELECT [Id] 
     FROM [dbo].[Products] 
     WHERE [Id] = SCOPE_IDENTITY(); 
END 

或者甚至沒有打擾在所有,因爲如果下面的結果集是空的,你現在已經知道了@@ROWCOUNT是:

SELECT [Id] 
    FROM [dbo].[Products] 
    WHERE [Id] = SCOPE_IDENTITY();