【问题标题】: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
  • 请适当标记

标签: c# .net-core entity-framework-core pomelo-entityframeworkcore-mysql ef-core-5.0


【解决方案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 属性。

【讨论】:

【解决方案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 有很多比这个更重要的功能。
猜你喜欢
  • 1970-01-01
  • 2023-03-04
  • 2011-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多