2013-03-21 33 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