[英]How to get Inner Join using navigation property in Entity Framework Core
我想生成一个查询,该查询使用导航属性对一对一关系使用内部联接。 这是针对我无法更改的现有数据库。
这是我的实体的样子:
[Table("Info")]
public class Info
{
[Key]
public int InfoId { get; set; }
public string Name { get; set; }
public virtual MoreInfo MoreInfo { get; set; }
}
[Table("MoreInfo")]
public class MoreInfo
{
[Key]
public int InfoId { get; set; }
public string OtherName { get; set; }
public int OtherNumber { get; set; }
}
这是我的查询:
var x = await context.Infos
.Select(info => new
{
info.InfoId,
info.Name,
info.MoreInfo.OtherName,
info.MoreInfo.OtherNumber
})
.ToListAsync();
这会生成此 SQL(使用左连接):
SELECT [info].[InfoId], [info].[Name], [info.MoreInfo].[OtherName], [info.MoreInfo].[OtherNumber]
FROM [Info] AS [info]
LEFT JOIN [MoreInfo] AS [info.MoreInfo] ON [info].[InfoId] = [info.MoreInfo].[InfoId]
如果 Info 中有不在 MoreInfo 中的记录,这将导致错误,这就是为什么我要进行内部联接。
我在 OnModelCreating 中尝试了各种选项,例如
modelBuilder.Entity<Info>()
.HasOne(p => p.MoreInfo)
.WithOne()
.IsRequired();
或将导航属性设置为必需
[Table("Info")]
public class Info
{
[Key]
public int InfoId { get; set; }
public string Name { get; set; }
[Required]
public virtual MoreInfo MoreInfo { get; set; }
}
或在查询中使用包含
var x = await context.Infos
.Include(info => info.MoreInfo)
.Select(info => new
{
info.InfoId,
info.Name,
info.MoreInfo.OtherName,
info.MoreInfo.OtherNumber
})
.ToListAsync();
但这些都没有改变加入。
我可以通过使用 join 函数而不是使用导航属性来获得内部联接,但如果可能的话,我想避免这种情况。 语法很难看,需要我将 MoreInfos 添加到上下文中。 如果可能,我想使用导航属性创建内部联接。
var x = await context.Infos
.Join(context.MoreInfos, info => info.InfoId, moreInfo => moreInfo.InfoId, (info, moreInfo) =>
new
{
info.InfoId,
info.Name,
moreInfo.OtherName,
moreInfo.OtherNumber
})
.ToListAsync();
生成正确的 SQL
SELECT [info].[InfoId], [info].[Name], [moreInfo].[OtherName], [moreInfo].[OtherNumber]
FROM [Info] AS [info]
INNER JOIN [MoreInfo] AS [moreInfo] ON [info].[InfoId] = [moreInfo].[InfoId]
但我宁愿使用导航属性。
当前(EF Core 2.1.2)无法使用提供的模型。
真正的一对一关系在技术上无法强制执行,因此 EF Core IsRequired
表示依赖实体 FK 是否可为空。 对于像您这样的共享 PK 协会来说,这始终是正确的。 请注意, Info
是主体,而MoreInfo
是从属,因此MoreInfo
需要Info
,反之亦然,这就是 EF Core 在从主体导航到从属时生成左外连接的原因。
您可以做的一件事是包含非空条件:
.Where(info => info.MoreInfo != null)
当在 EF6 查询(带有一些中间投影)中使用时,能够让 EF6 查询转换器使用内连接。 EF Core 仍然不够智能,因此所有这些技巧都不起作用。 它生成带有右侧键IS NOT NULL
过滤器的左外连接。 这等效于内部联接,并且可能会由 SQL 查询优化器进行处理。 但是还是...
当前获得内部联接的唯一方法(当然手动联接除外)是从关系的另一端导航,即从依赖关系到所需主体。 无需从上下文公开DbSet<MoreInfo>
, Set<TEntity>()
方法可用于访问任何实体集。 不过,您需要一个反向导航属性:
public class MoreInfo
{
// ...
public virtual Info Info { get; set; }
}
当然还有更新的流畅配置:
modelBuilder.Entity<Info>()
.HasOne(e => e.MoreInfo)
.WithOne(e => e.Info)
.HasForeignKey<MoreInfo>(e => e.InfoId);
现在查询可以改写为:
var x = await context.Set<MoreInfo>()
.Select(moreInfo => new
{
moreInfo.Info.InfoId,
moreInfo.Info.Name,
moreInfo.OtherName,
moreInfo.OtherNumber
})
.ToListAsync();
这将生成所需的内部连接:
SELECT [moreInfo].[InfoId], [moreInfo.Info].[Name], [moreInfo].[OtherName], [moreInfo].[OtherNumber]
FROM [MoreInfo] AS [moreInfo]
INNER JOIN [Info] AS [moreInfo.Info] ON [moreInfo].[InfoId] = [moreInfo.Info].[InfoId]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.