2012-11-02 21 views
0

在開發使用EF作爲ORM的ASP.NET MVC解決方案時,我開始構建一個罕見的bug(或Heisenbug,因爲它完全消失,分步調試)。Views中的隱式EF查詢導致與DataReader相關的InvalidOperationException

在SO,我發現我的錯誤的來源可以是:

  • DbContext是在我的域模型的靜態懶惰<> -wrapped字段,因而受到using(){}成語多個結果集。
  • 我的視圖本身會調用HtmlHelper自定義擴展方法,其中包含EF查詢(例如DropDown選擇填充,縮略圖鏈接生成等)。

通過將multipleactiveresultsets=True添加到我的connectionString定義中即可解決第一個問題。

後者假設需要切換到ViewModels,預填充在控制器操作中。但是,在許多情況下,使用ViewModel會導致過度殺傷,因此需要添加虛擬副本創建代碼,這會導致維護開銷。

我的問題是:

  • 爲什麼EF查詢中查看導致There is already an open DataReader associated with this Command which must be closed first.錯誤?做他們
  • 如何停止視圖執行沒有完整的代碼庫重寫查詢?

回答

0

如果MultipleActiveResultsets未設置爲true Sql Server不允許打開多個DataReader。在EF中進行延遲加載時,例如當您訪問導航屬性時,將查詢發送到數據庫以填充屬性。現在,如果你有一個這樣的循環(客戶,訂單都是實體並有1 *關係):

foreach(var o in context.Orders) 
{ 
    Console.Write(o.Customer.Id); 
} 

有一個開放的DataReader爲context.Orders的結果是按需物化,然後o.Customer打開另一個DataReader以獲取相關的Customer實體。請注意,這樣會將很多查詢發送到數據庫。如果您執行上述操作,則可以按如下方式更改代碼:

foreach(var o in context.Orders.Include("Customer")) 
{ 
    Console.Write(o.Customer.Id); 
} 

包含強制加載相關實體。這樣你只有一個Sql查詢,但你可能會帶來很多數據。因此,在發送多個查詢和下載大量數據之間進行權衡。

一個窮人的解決方法,你所得到的例外是:

foreach(var o in context.Orders.ToList()) 
{ 
    Console.Write(o.Customer.Id); 
} 

雖然它修復異常也沒有任何優勢。首先,您需要爲訂單實體集合提供所有數據 - 即使您不打算全部使用它們,並且您仍然向數據庫發送大量查詢,因爲o.Customer會觸發延遲加載。

最後,您可以禁用延遲加載 - 這樣一來,當你在你的應用程序發送查詢到數據庫中,你會知道,因爲你永遠需要加載導航屬性,然後才能使用它們。延遲加載使得它很容易和方便,但有時你可能甚至不知道何時/你發送查詢到數據庫

+0

謝謝你這樣一個全面的答案!不過,我總是將'List <>'傳遞給我的View。問題是(可能)由我寫的Html助手擴展引發的。他們顯然是從View調用的並且有EF查詢(同時返回'Select <>'和'IEnumerable',由'Select()'返回)。它可能是問題的根源嗎? – berezovskyi

+0

我不知道如何回答這個問題,但是你的堆棧跟蹤可能會顯示你調用EF查詢的方法...... – Pawel