2013-03-21 32 views
2

要模擬延遲加載,我希望有一個遞歸方式通過Eager Loading加載完整的對象圖,以便在加載實體時,其所有相關數據是加載以及。我如何遞歸地包含所有可導航的屬性來模擬延遲加載

從MSDN文檔:

  • 包括單個參考:query.Include(E => e.Level1Reference)。
  • 要包含單個集合:query.Include(e => e.Level1Collection)。
  • 要包含引用,然後向下引用一個引用:query.Include(e => e.Level1Reference.Level2Reference)。
  • 要包含引用,然後向下包含一個集合:query.Include(e => e.Level1Reference.Level2Collection)。
  • 要包含一個集合,然後向下引用一個引用:query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Reference))。
  • 要包含一個集合,然後向下一級集合:query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection))。
  • 要包含一個集合,然後向下引用一個引用:query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Reference))。
  • 要包含一個集合,然後向下一級集合:query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection))。
  • 要包含一個集合,一個引用和一個引用兩個層次:query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Reference.Level3Reference))。
  • 要包括一個集合,一個集合和一個引用兩個級別:query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection.Select(l2 => l2.Level3Reference)))。

問:

我怎樣才能遞歸包括所有通航性能和建造這個爲通用倉庫方法,以便在需要時我可以得到一個實體的深對象圖,無論如果添加了新的特性?

+0

你可能不希望這樣做。查詢中包含的數量建議不要超過三個。尤其是當包含導致星形查詢時,結果集很快就會爆炸成難以管理的比例。 – 2013-03-21 22:25:02

+0

不可能與LINQ(到實體)。我聽說過的唯一方法是使用SQL公用表表達式(CTE)的存儲過程:http://stackoverflow.com/a/11621006/270591 – Slauma 2013-03-21 22:39:34

+0

這將是一次性方法,所以我並不擔心關於性能,我只是試圖確保我得到一個完整的實體圖(就像懶惰加載已啓用),現在,我通過手動包含所有可導航屬性來實現此功能,但是當模型更改時,我必須去更新我的GetFullEntity方法,這是我試圖解決這個問題。 – user1265146 2013-03-22 00:14:56

回答

0

如果您使用模型優先或數據庫優先,您可以編寫一些T4模板來使用edmx模型生成您需要的內容。這並不容易,但可能。

3

好吧,這是一個編輯的版本,應該滿足您的要求好一點:

private static void EnumerateAllIncludesList(DbContext context, IEnumerable entities, List<object> entitiesLoaded = null) 
{ 
    if (entitiesLoaded == null) 
     entitiesLoaded = new List<object>(); 

    foreach (var entity in entities) 
     EnumerateAllIncludesEntity(context, entity, entitiesLoaded); 

} 

private static void EnumerateAllIncludesEntity(DbContext context, object entity, List<object> entitiesLoaded) 
{ 
    if (entitiesLoaded.Contains(entity)) 
     return; 

    entitiesLoaded.Add(entity); 

    Type type = entity.GetType(); 
    var properties = type.GetProperties(); 

    foreach (var propertyInfo in properties) 
    { 
     var propertyType = propertyInfo.PropertyType; 

     bool isCollection = propertyType.GetInterfaces().Any(x => x == typeof(IEnumerable)) && 
          !propertyType.Equals(typeof(string)); 

     if (isCollection) 
     { 
      var entry = context.Entry(entity); 

      if(entry.Member(propertyInfo.Name) as DbCollectionEntry == null) 
       continue; 

      entry.Collection(propertyInfo.Name).Load(); 

      var propertyValue = propertyInfo.GetValue(entity); 

      if (propertyValue == null) 
       continue; 

      EnumerateAllIncludesList(context, (IEnumerable)propertyValue, entitiesLoaded); 
     } 
     else if ((!propertyType.IsValueType && !propertyType.Equals(typeof(string)))) 
     { 
      var entry = context.Entry(entity); 

      if (entry.Member(propertyInfo.Name) as DbReferenceEntry == null) 
       continue; 

      entry.Reference(propertyInfo.Name).Load(); 

      var propertyValue = propertyInfo.GetValue(entity); 

      if (propertyValue == null) 
       continue; 

      EnumerateAllIncludesEntity(context, propertyValue, entitiesLoaded); 
     } 
     else 
      continue; 
    } 
} 

你會使用這個像這樣:

using (var context = new MyContext()) 
{ 
    var result = context.People.Where(x => x.Id == 1).ToList(); 
    EnumerateAllIncludesList(context,result); 
} 
+0

除非我在答案中遺漏了某些東西,否則如果任何導航屬性具有相應的反轉屬性,則這將不起作用。假設您有一個帶有管理器的引用屬性的Employee實體,該管理器具有導航屬性Employees。包括(「Manager.Employees」),Include(「Manager.Employees.Manager」),Include(「Manager.Employees.Manager.Emplyees」) 。 <...>' – hvd 2013-03-22 15:07:47

+0

我錯過了一些東西:你沒有看集合元素的屬性,所以這個特定的例子不會按照我所描述的方式失敗,但是如果'employee.Manager'又是一個員工,你仍然會得到'.include(「Manager」)。Include(「Manager.Manager」)。包括<...>' – hvd 2013-03-22 15:09:41

+0

是的,這是我用這段代碼所遇到的問題。我有鏈接回父實體的子實體,並且此代碼會導致無窮循環,如@ hvd指出的那樣。我們能否以某種方式追蹤以確保我們在無限循環中停止重複發生? – user1265146 2013-03-22 21:55:15