【问题标题】:Entity Framework 7 multiple levels of child tablesEntity Framework 7 多级子表
【发布时间】:2016-07-18 20:24:12
【问题描述】:

我刚刚开始使用 EF7 (CORE),并且正在努力寻找以下内容的正确实现。假设我有一个包含多个子表的表,这些子表又具有孙表(而这些表又具有外键表)。如果我想访问所有内容,我需要这样的东西

 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 来完成我需要的事情,是否有最佳实践?

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

谢谢

【问题讨论】:

    标签: entity-framework-core


    【解决方案1】:

    将此扩展方法添加到您的项目中,加载方法存在于 ef 6.x 中,但尚未在 ef core 中实现:

    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);
    

    根据您的用例,可以在加载 TABLE_A 的第一个查询中包含或 ThenInclude 一些导航。

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

    【讨论】:

    • 这对收藏有什么作用? TABLE_A 是一个集合......每个实体都有一个 TABLE_B 列表。如果我要使用_context.Entry(&lt;List of table A&gt;).Load(b =&gt; b.&lt;Table_B&gt;);,我会收到错误“IQueryable”不包含“TABLE_B”的定义,并且无法找到接受“IQueryable”类型的第一个参数的扩展方法“TABLE_B”
    • _context.Entry 仅获取单个对象,您首先加载所有 TABLE_A 列表,并且每当需要 TABLE_A 列表中的每个项目时,使用此代码例如: var item =TABLE_A.First(); context.Entry(item).Load(b => b.TABLE_B);
    • 哇,这是一个糟糕的解决方案。如果我在一个视图中渲染 300 个项目,并显示来自 10 个 FK 表的数据,我将在后台有效地运行 3000 个查询。
    • 在实体框架中,查询数据存在两种方式,即您在问题中使用的急切加载,但会发生极端数据加载和查询,以及我在回答中建议的延迟加载,具体取决于您的问题,选择在途中或两种方式之间的贸易,
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-02-19
    • 2016-08-12
    • 2017-03-09
    • 1970-01-01
    • 2016-04-12
    • 1970-01-01
    • 2016-01-28
    相关资源
    最近更新 更多