2016-07-15 59 views
0

我刚刚开始使用EF7(CORE),并且正在努力寻找以下的正确实现。假设我有一个包含多个子表的表,而这些子表又具有孙表(并且这些表又具有外键表)。如果我想获得的一切我需要这样的实体框架7多级子表

TABLE_A.Include(c => c.TABLE_B).ThenInclude(co => co.TABLE_C) 
           .ThenInclude(coi => coi.TABLE_D) 
           .ThenInclude(coia => coia.TABLE_E) 
     .Include(c => c.TABLE_B).ThenInclude(co => co.TABLE_F) 
           .ThenInclude(coa => coa.TABLE_G) 
           .ThenInclude(coaAcc => coaAcc.TABLE_H) 
           .ThenInclude(coaAccInt => coaAccInt.TABLE_D) 
           .ThenInclude(coaAccIntAgent => coaAccIntAgent.TABLE_E) 

现在我明白了链接的包括包括我所有的子表的必要性......但我看它触发的幕后SQL并发布了11条SQL语句。这似乎非常低效。

这是做这件事的最好方法吗?我现在收到了一个新的要求,为TABLE_B添加3个子表...所以我需要更多的包含...因此更多的选择在幕后运行。

我明白我在做什么的背后的逻辑..并且了解延迟加载目前在EF7中不受支持,但这似乎不是一种非常有效的做事方式,因为我可以编写一个存储过程一口气做到这一点。

是否有像这样或某事的最佳实践我没有掌握如何使用EF7来做我需要的东西?

任何帮助或指导将不胜感激!

感谢

回答

0

该扩展方法添加到您的项目,Load方法在EF 6.x的存在,但尚未在EF核心没有实现:

public static void Load<TSource, TDestination>(this EntityEntry<TSource> entry, Expression<Func<TSource, IEnumerable<TDestination>>> path, Expression<Func<TDestination, TSource>> pathBack = null) where TSource : class where TDestination : class 
{ 
    var entity = entry.Entity; 
    var context = entry.Context; 
    var entityType = context.Model.FindEntityType(typeof(TSource)); 
    var keys = entityType.GetKeys(); 
    var keyValues = context.GetEntityKey(entity); 
    var query = context.Set<TDestination>() as IQueryable<TDestination>; 
    var parameter = Expression.Parameter(typeof(TDestination), "x"); 
    PropertyInfo foreignKeyProperty = null; 

    if (pathBack == null) 
    { 
     foreignKeyProperty = typeof(TDestination).GetProperties().Single(p => p.PropertyType == typeof(TSource)); 
    } 
    else 
    { 
     foreignKeyProperty = (pathBack.Body as MemberExpression).Member as PropertyInfo; 
    } 

    var i = 0; 

    foreach (var property in keys.SelectMany(x => x.Properties)) 
    { 
     var keyValue = keyValues[i]; 

     var expression = Expression.Lambda(
      Expression.Equal(
       Expression.Property(Expression.Property(parameter, foreignKeyProperty.Name), property.Name), 
       Expression.Constant(keyValue)), 
      parameter) as Expression<Func<TDestination, bool>>; 

     query = query.Where(expression); 

     i++; 
    } 

    var list = query.ToList(); 

    var prop = (path.Body as MemberExpression).Member as PropertyInfo; 
    prop.SetValue(entity, list); 
} 

public static object[] GetEntityKey<T>(this DbContext context, T entity) where T : class 
{ 
    var state = context.Entry(entity); 
    var metadata = state.Metadata; 
    var key = metadata.FindPrimaryKey(); 
    var props = key.Properties.ToArray(); 

    return props.Select(x => x.GetGetter().GetClrValue(entity)).ToArray(); 
} 

那么当你需要每个导航性能,使用前先呼叫负载(只是一次对任何导航性能)的方法休耕:

//for first item 
var item = TABLE_A.First(); 
context.Entry(item).Load(b => b.TABLE_B); 

根据你的使用情况,可以包括或ThenInclude一些导航在第一个查询中加载TABLE_A。

加载扩展方法Source Link更多示例

+0

这对于收藏起什么作用? TABLE_A是一个集合...并且每个实体都有一个TABLE_B列表。如果我是使用 '_context.Entry(<表A的一览>)负载(B => b 。);' 我得到错误 '的IQueryable ' 不包含关于“表-B的定义'并且没有扩展方法'TABLE_B'接受类型'IQueryable '的第一个参数可以找到 – rborob

+0

_context.Entry只获取单个对象,首先加载所有TABLE_A列表,并且每当需要时TABLE_A列表中的每个项目,使用此代码ex :var item = TABLE_A.First(); context.Entry(item).Load(b => b.TABLE_B); <事实上,类似于LazyLoadining,但显式加载导航 –

+0

哇,这是一个可怕的解决方案。如果我在一个视图中渲染300个项目,并显示来自10个FK表格的数据,我将在幕后有效地运行3000个查询。 – rborob