2015-12-30 45 views
2

是否可以手動將現有對象分配給實體框架(db第一個)對象的導航屬性?手動將現有對象分配給實體框架導航屬性

問題的上下文是,我試圖帶回一個帶有所有子代和子代的對象列表(問題嚴重),以便在上下文處理完後可以在內存中使用完整圖表。

我試圖用這樣的事情通過.Include()陳述這樣做:

using (var ctx = new MyEntities()) 
    { 
    myParents = ctx.Parents 
     .Where(p => MyFilter(p)) 
     .Include(p => p.Children) 
     .Include(p => p.Children.Select(c=>c.Grandchildren)) 
     .Include(p => p.Children.Select(c=>c.Grandchildren.Select(g=>g.GreatGrandChildren))); 

    } 

,但生成的查詢運行,因爲與使用嵌套包括語句(如在很多地方,包括this blog解釋已知的性能問題太慢)。

我可以將父母,子女和孫子女退回而沒有表現問題 - 我只在包含最後的.Include()陳述給偉大的孩子時遇到麻煩。

我可以很容易地得到GreatGrandChildren對象從與第二單獨的查詢數據庫恢復通過建立從已經獲取的孫子GrandChildrenIds的列表,並做類似:

greatGrandKids = ctx.GreatGrandChildren.Where(g=>ids.Contains(g.GrandChildId)).ToList(); 

但現在,一旦我處置在上下文中,我不能像grandChildA.GreatGrandChildren那樣做,而不會觸及對象上下文處置的異常。

我可能有多達幾千個GrandChildren對象,所以我真的想要避免往返數據庫的往返取回GreatGrandChildren,每個GrandGild對象只需使用.Load()即可排除每個GrandChild對象,對吧?

我可以要麼只是查找所需greatgrandchildren從greatGrandKids每個我需要他們在我後面的代碼,甚至通過添加新的(非映射)屬性,如.GreatGrandChildrenLocalGrandChild類和分配時間可行性的解決這個工作他們都在前面,但這些都感覺 kludgy &醜。我更願意找到一種方法,以便能夠訪問每個GrandChild對象上現有的.GreatGrandChildren導航屬性。

試圖明顯的是這樣分配的導航屬性:

grandchild.GreatGrandChildren = greatGrandKids 
    .Where(g=>g.GrandChildId == grandChild.Id) 
    .ToList(); 

失敗過,當我再嘗試訪問grandchild.GreatGrandChildren(仍然給對象設置除外)。

所以我的問題是:

有沒有一種方法,我可以分配以這樣的方式,以使它們可我已經從數據庫中檢索到孫子對象的.GreatGrandChdildren導航屬性現有GreatGrandChdildren對象(僅在讀取操作時需要)在上下文處理後?

(或者確實有不同的問題解決方案?)

+0

你正在使用什麼樣的應用程序?網?的WinForms?通常情況下,你不需要在上下文處理後進行這樣的操作。如果您使用「選擇」來僅選擇必要的屬性會怎樣?然後,如果需要插入新的子對象,則可以在不加載父對象的情況下執行此操作,而只使用正確的ID。顯示您使用的代碼示例 –

+0

@FabioLuz這是用於後端服務。在上下文處理後,需要訪問後代屬性並不罕見。我需要這些後代屬性來使用關聯的數據執行一些操作。我需要每一個與過濾父項關聯的'GreatGrandChildren'對象(過濾後只有幾個父項)來執行這些操作,因此建議'.Select()'不會觸及數據庫幾千次我害怕。如果可以,我想避免這樣做。 –

+0

對不起,我想我錯了。如果你提供一些示例代碼會更容易,包括你在哪裏得到異常 –

回答

0

如果禁用代理的創建:

ctx.Configuration.ProxyCreationEnabled = false; 

然後讀取和/寫入到該導航屬性的工作原理完全如預期,但不嘗試加載懶洋洋的實體和投擲物體佈置例外。

所以我們有類似:

using (var ctx = new MyEntities()) 
{ 
    myParents = ctx.Parents 
     .Where(p => MyFilter(p)) 
     .Include(p => p.Children) 
     .Include(p => p.Children.Select(c=>c.Grandchildren)); 
     //skip the final GreatGrandChildren include statement 

    //get the associated grandchildren & their ids: 
    var grandKids = myParents.SelectMany(p=>p.Children) 
     .SelectMany(c=>c.Grandchildren) 
     .ToList(); 
    var ids = grandKids.Select(g=>g.Id)).ToList(); 

    //Get the great grandkids: 
    var greatGrandKids = ctx.GreatGrandChildren 
     .Where(g=>ids.Contains(g.GrandChildId)).ToList(); 

    //Assign the greatgrandchildren to the grandchildren: 
    foreach (grandChild in grandKids) 
    { 
     grandChild.GreatGrandChildren = greatGrandKids 
      .Where(g=>g.GrandChildId == grandChild.Id) 
      .ToList(); 
    } 

} 

現在我們可以沒有擊中設置例外情況下訪問上下文之外的.GreatGrandChildren財產。雖然這仍然感覺有點混亂,但它比使用原始Include()聲明或在每個GrandChild上致電.Load()要便宜得多。

N.B.由於這些對象僅用於讀取操作,並且我不需要延遲加載,因此在我的情況下關閉代理創建沒有負面影響。如果寫入操作和/或延遲加載也是必要的,那麼我們還需要考慮在給定的EF上下文中關閉此功能的含義。