繁体   English   中英

如何使用 Entity Framework Core 中的导航属性获取内部连接

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM