2010-11-12 54 views
3

我首先在通用存儲庫中使用ef4代碼。我的倉庫有類似如下的選擇方法:EF4 linq非null對象上的NullReferenceException

public IEnumerable<T> Select(Func<T, bool> predicate) 
{ 
    return objectSet.Where(predicate); 
} 

我用下面的代碼

pushQueueRepository.Select(x => x.User.ID == user.ID && x.PageID == pageID); 

稱之爲*注 - pushQueueRepository已正確實例化。

當我運行這個我得到一個NullReferenceException。當我在拋出異常後的調試中看到它時,它顯示錯誤是x.User.ID == user.ID.當我將鼠標移到x.User上時,它是空的。然而,當我展開x我們有一個用戶對象在x.User(非空),確實有一個id。

FYI x是PushQueue對象定義爲這樣:

public class PushQueue : IEntity 
{ 
    ... 

    [Required] 
    public User User { get; set; } 

    ... 
} 

這似乎並不正確,我失去的東西嗎?

謝謝。

回答

2

之所以得到例外的是檢查,因爲您加載所有的在內存中,然後嘗試應用您的謂詞PushQueues:x => x.User.ID == user.ID因爲延遲加載由代碼啓用後,將x.User不能偷懶加載因此異常被拋出。您尚未將用戶導航屬性標記爲虛擬,因此它未選擇進入EF延遲加載。當在VS調試模式展開,你是明確加載它,但是在運行時它不是延遲加載,這就是爲什麼你看到的,當你展開它的填充。

要解決此問題,您需要更改Select方法的簽名,因爲這是主要問題:您要傳遞Func<T, bool>,而需要傳遞Expression<Func<T, bool>>。基本上你想在數據存儲沒有在內存中執行的謂語,所以你需要將代碼改成這樣:

public IEnumerable<T> Select(Expression<Func<T, bool>> predicate) 
{ 
    return objectSet.Where(predicate); 
} 

當然,或者你可以保持選擇的方法,因爲它是現在讓懶惰加載,這樣一來,NullReferenceException異常會消失,但它會導致在自EF運行時的可怕表現將試圖延遲加載用戶的每一個PushQueue對象,然後運用你的斷言:

public virtual User User { get; set; }  
+0

這對我來說非常合適。謝謝您的幫助。 – jimox 2010-11-15 06:22:41

+0

好解釋!請注意,您不能輸入任何謂詞的任意函數。它需要由數據存儲直接計算。 – 2010-11-16 16:50:06

0

很有可能通過擴展x,導致其他屬性被評估,然後填充x.User

當然,如果這是一個EF庫,我期望反正數據庫要執行的實際查詢,讓你在查詢故障的情況下看到的是有點不尋常呢。你有沒有嘗試過在SQL中執行什麼查詢?

+0

當我簽入profiler它本質上運行SELECT * FROM [PushQueues]。這是我所期望的。結果是正確的。由於用戶不被標記爲虛擬不應該立即加載? – jimox 2010-11-12 07:20:09

+0

如果x.User最初沒有填充,那麼是否有一些我需要做的事情來獲得EF來允許我查詢外鍵? – jimox 2010-11-12 07:33:21

0

我認爲你需要使用在.include:

pushQueueRepository.Include("User").Select(x => x.User.ID == user.ID && x.PageID == pageID) 

.INCLUDE力量EF加載用戶對象向右走,否則就會延遲加載,從而無法對您的User.ID比較。

具有在.include一個字符串是不是很優雅,遺憾的是沒有編譯時檢查。你可以做這樣的事情:

pushQueueRepository.Include(typeof(User).Name).... 

不是很優雅下去,但至少它是由編譯器;-)

+0

Torben,感謝您的幫助。雖然我相信這會起作用,但我使用了Morteza的建議,因爲它在這種情況下效果更好。乾杯。 – jimox 2010-11-15 06:25:07