2014-01-07 104 views
1

我有實體框架生成以下類:LINQ到實體延遲加載

public partial class Branch 
{ 
    public short Id { get; set; } 
    public short CompanyId { get; set; } 
    public string Code { get; set; } 
    public string Title { get; set; } 

    public virtual Company Ts_Companies { get; set; } 
} 

我有以下的方法,這需要所有分支出來的數據庫:

public Branch[] LoadBranches(int companyId, int page, int limit, string search, string sort, string sortOrder) 
    { 
     using (var dbContext = new TimeShedulerEntities()) 
     { 
      var _branches = (from ct in dbContext.Branches 
          where ct.Title.Contains(search) || ct.Code.Contains(search) 
          select ct).OrderBy(c => c.Title).Skip((page - 1) * limit).Take(limit); 
      return _branches.ToArray(); 
     } 
    } 

在我模型設計器我看到Lazy Loading設置爲true,但是當我遍歷分支時,屬性Ts_Companies爲null。此外,我得到以下異常:

型「System.ObjectDisposedException」的異常出現在 EntityFramework.dll但在用戶代碼中沒有處理

附加信息:ObjectContext的實例已佈置 和不能再用於需要連接的操作。

我忘了什麼嗎?

+2

您的地方,這種情況下的參考範圍以外的情況下實體正在使用「使用」範圍之外的分支,因此它已被處置。 – Jasen

+1

它告訴你錯誤信息中的錯誤。背景已經處理完畢。要麼在你完成查詢之前不要處理上下文,要麼不要懶惰地加載你需要的信息。 – Servy

+0

@Servy我同意你的意思,但不應該'返回_branches.ToArray()'強制執行查詢?這發生在使用語句的範圍內,所以我真的不知道他可以在哪裏訪問處理的數據庫上下文。 – evanmcdonnal

回答

3

您在函數中創建並處理了上下文,因爲它位於using語句中。每個實體都知道它是從哪個上下文創建的,因此可以進行延遲加載。

當您訪問Ts_Companies屬性時,實體意識到它尚未加載該屬性,因爲它可能是導航屬性,並試圖請求其ObjectContext(TimeShedulerEntities)加載該屬性。但是,背景已經處理完畢,因此導致了這種例外。

您需要修改您的查詢,如下所示爲「預加載」的Ts_Companies:

var _branches = (from ct in dbContext.Branches.Include("Ts_Companies") 
         where ct.Title.Contains(search) || ct.Code.Contains(search) 
         select ct).OrderBy(c => c.Title).Skip((page - 1) * limit).Take(limit); 

這將可能需要相當多一點的時間取決於大小加載的Ts_Companies對象和多少你最終立即恢復,但實體將停止詢問其對象上下文加載Ts_Companies,因爲您已經加載了它們。

附註:我發現創建和處理對象上下文在每個方法的基礎上會導致實體在函數外部傳遞時出現問題。如果你想創建和銷燬每個函數中的對象上下文,你可能希望讓這個函數返回一個不是實體的東西。換句話說,有一個可以從一個實體構建的對象,並具有您需要的屬性,但沒有它引用該實體。在Java中,這些通常稱爲數據傳輸對象(DTO)。你失去了實體框架的讀寫能力,但你沒有意想不到的ObjectDisposedException飛到了這個地方。

當你要求一個實體與另一個實體相關聯時(例如,將實體添加到另一個實體的ICollection屬性),當它們來自不同的objectcontext時,問題就出現了。這會讓你很頭疼,因爲在執行該操作之前,你必須手動將對象附加到相同的上下文中。此外,您無法將更改保存到這些實體而無需手動將它們附加到不同的上下文。

我對我會怎麼做意見:

我發現它更容易要麼有一個包含所有這些數據庫訪問函數控制上下文的生命期的對象(也就是已經包含的對象是IDisposable和處置期間,銷燬上下文),或者根本就不返回實體,並且使數據存儲被讀取爲舊的,基本上沒有任何修改能力的寫入。

例如,我有我的對象(我將它稱爲我的數據訪問對象)與一堆獲取數據庫對象的方法。這些方法返回實體。數據訪問對象也有一個SaveChanges方法,它只是簡單地調用上下文的SaveChanges方法。數據訪問對象包含受保護屬性中的上下文,並將其保留,直到數據訪問對象本身被處置。除了數據訪問對象之外,沒有人可以觸摸它的上下文。此時,通過手動調用「Dispose」來處理上下文。如果這是您的用例,那麼數據訪問對象可以在using語句中使用。

在任何情況下,它可能是最好避免將附着在它們的上下文存在,因爲實體框架保持遍佈在各個實體

+0

我的所有方法都是以這種方式創建的,我有一個類庫,其中包含一個DbQueries類,它包含所有類似LoadBranches的方法。這種方式不正確嗎?我應該把創建上下文的塊放在我的網頁的代碼隱藏中嗎? – Mivaweb

+0

這種方式非常好。問題在於在每種方法中處理上下文。如果您將'DbQueries'類設置爲'IDisposable',並讓所有方法都在相同的上下文中運行,只要您的DbQueries類保持不變,只要您需要這些實體即可消除ObjectDisposedException的問題。考慮一旦他們的父上下文被處置,實體將被「處置」。 –

+0

好的,我將在模型設計器中設置「延遲加載」,然後手動調用分離的方法加載公司數據。 – Mivaweb

0

但是你沒有加載Ts_Companies,使用Eager Loading代替:

var _branches = dbContext.Branches 
         .Where(b => b.Title.Contains(search) || b.Code.Contains(search)) 
         .Include("Ts_Companies") 
         .OrderBy(c => c.Title) 
         .Skip((page - 1) * limit) 
         .Take(limit); 

而且我在MVC項目System.ObjectDisposedException,之前在同樣的問題來了,我沒有使用using塊,而不是我定義我的如果我需要返回並使用一個數組(在我的視圖)我使用該上下文。如果我需要只是更新一些信息,那麼我已經使用using blocks.I希望這可以幫助。