使用带有条件过滤器的 Include() 连接两个表

Posted

技术标签:

【中文标题】使用带有条件过滤器的 Include() 连接两个表【英文标题】:Joining two tables using Include() with conditional filter 【发布时间】:2019-12-15 07:09:19 【问题描述】:

我有现有代码要更新。我想加入另一个名为Table3 的表。 由于查询有一个 includeTable2,我想添加另一个带有条件过滤器的 .Include 并避免左连接。

当我添加.include.where 时,我无法访问t3Id。 Intellisense 只显示表格 Table3 而不是 Id 字段。

我错过了语法吗?谢谢。

Table1 有一个名为 t1Id 的密钥。

 var query = (from e in ctx.Table1
    .Include(r => r.Table2.Select(p => p.name))
    .Include(rj => rj.Table3).Where(s => s.t1Id == t3Id)
    select e).ToList();

Table1 将具有以下内容:

Id  name  
1   Joe   
2   Mary
3   Harry

Table3 将具有以下内容:

t1Id   title
3      staff
3      fulltime

预期结果:

1  Joe
2  Mary
3  Harry  [2, staff, 3, fulltime]

由于 Harry 在映射表中有一条记录,他将有一个 Table3 行数组。

【问题讨论】:

是应该用于rj.Table3的where子句 报错后,我不这么认为。我试图弄清楚如何计算 t1Id == t3Id 的 Table3 并返回每个 T1 行的所有 T3 数据。 我不清楚你要什么。您能否向我展示一个表 1、表 2 和表 3 的基本示例数据集以及您正在寻找的预期结果? HI Train,刚刚添加了表格插图 使用 .net 4.7 和实体框架 6.3.0 【参考方案1】:

我刚刚注意到您的 EF 评论。 EF 属性Table1.Table3 应该已经加载了相关实体,而无需在使用包含时使用 where 子句。

试试这个扩展方法

    public static IEnumerable<TEntity> GetAllIncluding(this params Expression<Func<TEntity, object>>[] includesProperties)
    
         return includesProperties.Aggregate<Expression<Func<TEntity, object>>,
         IQueryable<TEntity>>(_dbSet, (current, includeProperty)
         => current.Include(includeProperty));
    

使用功能

 var data = table1.GetAllIncluding(x => x.Table2, y => y.Table3);

这应该已经加载了相关实体而无需过滤。

【讨论】:

【参考方案2】:

最好尽可能使用Select 而不是IncludeSelect 将允许您仅查询您真正计划使用的属性,从而更快地将所选数据从数据库管理系统传输到您的进程。

例如,如果您查询“Schools with their Students”,Student 将有一个外键,其值等于 School 的主键。因此,如果您有 School 10,您现在将拥有其所有 5000 名学生的 SchoolId,其值为 10。发送相同的值 10 超过 5000 次有点浪费。

查询数据时,始终使用 Select。仅当您计划更新获取的数据时才使用 Include。

您的查询(在方法语法中):

var result = dbContext.Table1
    .Where(table1Element => ...)    // only if you don't want all elements of Table1 
    .Select(table1Element => new
    
         // only select the table1 elements that you plan to use:
         Id = table1Element.Id,
         Name = table1Element.Name,

         // Select the items that you want from Table 2:
         Table2Items = table1Element.Table2
                       .Where(table2Element => ...) // only if you don't want all table2 elements
                       .Select(table2Element => new
                       
                            // Select only the properties of table2 you plan to use:
                            Id = table2Element.Id,
                            Name = table2Element.Name,
                            ...

                            // the following not needed, you already know the value:
                            // Table1Id = table2Element.table1Id, // foreign key
                       )
                       .ToList(),

         // Table3: your new code:
         Table3Items = table1Element.Table3
                       .Select(table3Element => new
                       
                            // again: only the properties you plan to use
                            Id = table3Element.Id,
                            ...

                            // the following not needed, you already know the value:
                            // Table1Id = table3Element.table1Id, // foreign key
                       )
                       .ToList(),
    );

您发现读者更容易看到他获得了哪些属性?如果其中一个表被展开,那么新的属性就不会在这个查询中被获取,毕竟:用户显然不需要新的属性。在此功能的规范中也没有描述它们。

注意:因为我使用了new,所以我的类型是匿名类型。你只能在你的函数中使用它们。如果需要返回获取的数据,请将数据放在已知的类中:

.Select(table1Element => new Table1Class()

    Id = table1Element.Id,
    Name = table1Element.Name,
    ...
);

再次:考虑不要填写外键,因为您可能不会使用它们

【讨论】:

以上是关于使用带有条件过滤器的 Include() 连接两个表的主要内容,如果未能解决你的问题,请参考以下文章

连接两个表而不根据连接条件过滤数据

带有 if 条件的内连接

优化数据库有啥更好的方法:使用带有不同过滤器的一堆存储过程,还是使用带有 if 条件的单个存储过程?

带有 OR 条件的 SQL 多连接

使用内部连接将两个或多个条件定义到存储过程中?

使用带有多个语句的 data.table 进行条件过滤