如何递归地包含所有可导航属性以模拟延迟加载

时间:2013-03-21 21:18:48

标签: c# entity-framework entity-framework-5

为了模拟延迟加载,我希望有一个递归的方法通过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) ))。

问题:

如何递归地包含所有可导航属性并将其构建到通用存储库方法中,以便在需要时可以获得实体的深层对象图,无论是否添加了新属性?

2 个答案:

答案 0 :(得分: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);
}

答案 1 :(得分:0)

如果您使用模型优先或数据库优先,您可以编写一些T4模板以使用edmx模型生成所需内容。这并不容易,但可能。