实体框架在外键条件下产生左连接

Posted

技术标签:

【中文标题】实体框架在外键条件下产生左连接【英文标题】:Entity framework produces left join when condition on foreign key 【发布时间】:2021-04-23 02:55:08 【问题描述】:

我有 2 个模型:

public class User

    public int Id  get; set; 

    [Required] 
    [MaxLength(50)]
    public string Email  get; set; 

    [Required] 
    [MaxLength(100)] 
    public string Password  get; set; 

public class Questionnaire

    public int Id  get; set; 

    [Required] 
    [MaxLength(500)] 
    public string Title  get; set; 

    public User User  get; set; 

我想用这个查询来检索某个用户的所有问卷:

List<Questionnaire> questionnaires = this._dbContext.Questionnaires.Where(a => a.User.Id == 1).ToList();

它可以工作,但是实体框架会产生这个 sql 查询:

SELECT `q`.`Id`, `q`.`Title`, `q`.`UserId`
FROM `Questionnaires` AS `q`
     LEFT JOIN `Users` AS `u` ON `q`.`UserId` = `u`.`Id`
WHERE `u`.`Id` = 1;

在我看来,左连接是不必要的。请问有什么解决方法可以避免这种左连接吗?提前谢谢你。

【问题讨论】:

EF 核心版本?或 EF 6 Microsoft.EntityFrameworkCore 5.0.2 请适当标记 【参考方案1】:

更多信息:

If you choose not to explicitly include a foreign key property in the dependant end of the relationship, EF Core will create a shadow property using the pattern Id。如果您查看Questionnaire 数据库表,UserId 列存在并且它已由 EF 核心创建为影子外键。

当您在 where 子句 _dbContext.Questionnaires.Where(a =&gt; a.User.Id == 1) 中引用 User 时,EF Core 会将 linq 查询转换为 TSQL 左连接。

你也可以使用影子属性来定义外键:

protected override void OnModelCreating(ModelBuilder builder)

    builder.Entity<Questionnaire>()
        .Property<int>("UserId");

    builder.Entity<Questionnaire>()
        .HasOne(e => e.User)
        .WithMany()
        .HasForeignKey("UserId")
        .OnDelete(DeleteBehavior.SetNull);

现在左连接将替换为内连接:

SELECT [q].[Id], [q].[Title], [q].[UserId]
      FROM [Questionnaires] AS [q]
      INNER JOIN [Users] AS [c] ON [q].[UserId] = [c].[Id]
      WHERE [c].[Id] = 1

为了避免不必要的加入,正如@Guru Stron 所说,您需要在Questionnaire 类上公开UserId 属性。

【讨论】:

非常有趣。检查以确保我跟随你,它暴露了与导航属性相关联的键,这会有所不同。换句话说,您实际上不必在 LINQ 查询中引用这个公开的键? EF will introduce a shadow property when a relationship is discovered but no foreign key property is found in the dependent entity class。对于性能问题,最好公开外键。【参考方案2】:

您需要手动公开Questionnaire 上的UserId 属性:

public class Questionnaire

    public int Id  get; set; 

    [Required] 
    [MaxLength(500)] 
    public string Title  get; set; 

    public int UserId  get; set; 
    public User User  get; set; 

并在查询中使用它而不是a.User.Id

var questionnaires = this._dbContext.Questionnaires
    .Where(a => a.UserId == 1) // use UserId instead of User.Id
    .ToList(); 

【讨论】:

这是非常违反直觉的。你知道这种行为的原因吗? @aluan-haddad 看看我的回答。 @AluanHaddad 不确定(不必费心查看源代码),但我会说原因是表达式树的访问方式,显然您可以实现 User.Id 以在不加入的情况下进行处理(请注意,即使暴露了UserIdUser.Id 仍然会在连接中结束),但这会使 EF Core 代码更加复杂。也许稍后会支持,但我敢打赌 ATM 有很多比这个更重要的功能。

以上是关于实体框架在外键条件下产生左连接的主要内容,如果未能解决你的问题,请参考以下文章

实体框架核心,左连接

实体框架左外连接和分组:ORA-00907:缺少右括号

如何在实体框架中的两个表之间进行左连接操作时从左表中选择唯一行

在实体框架中使用视图

左连接 ON 条件和 Doctrine 中的其他条件语法

我可以在不返回条件列的情况下进行左连接吗?