实体框架在外键条件下产生左连接
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 => 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
以在不加入的情况下进行处理(请注意,即使暴露了UserId
,User.Id
仍然会在连接中结束),但这会使 EF Core 代码更加复杂。也许稍后会支持,但我敢打赌 ATM 有很多比这个更重要的功能。以上是关于实体框架在外键条件下产生左连接的主要内容,如果未能解决你的问题,请参考以下文章