简体   繁体   English

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

[英]How to get Inner Join using navigation property in Entity Framework Core

I want to generate a query that uses an inner join for a one to one relationship using a navigation property.我想生成一个查询,该查询使用导航属性对一对一关系使用内部联接。 This is for an existing database that I cannot change.这是针对我无法更改的现有数据库。

Here are what my entities look like:这是我的实体的样子:

[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; }
}

and here is my query:这是我的查询:

var x = await context.Infos
    .Select(info => new
    {
        info.InfoId,
        info.Name,
        info.MoreInfo.OtherName,
        info.MoreInfo.OtherNumber
    })
    .ToListAsync();

this produces this SQL (which uses a left join):这会生成此 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]

This will cause an error if there are records in Info that are not in MoreInfo, which is why I want to do an inner join.如果 Info 中有不在 MoreInfo 中的记录,这将导致错误,这就是为什么我要进行内部联接。

I have tried various option in OnModelCreating such as我在 OnModelCreating 中尝试了各种选项,例如

modelBuilder.Entity<Info>()
    .HasOne(p => p.MoreInfo)
    .WithOne()
    .IsRequired();

or setting the navigation property as Required或将导航属性设置为必需

[Table("Info")]
public class Info
{
    [Key]
    public int InfoId { get; set; }
    public string Name { get; set; }
    [Required]
    public virtual MoreInfo MoreInfo { get; set; }
}

or using an include in the query或在查询中使用包含

var x = await context.Infos
    .Include(info => info.MoreInfo)
    .Select(info => new
    {
        info.InfoId,
        info.Name,
        info.MoreInfo.OtherName,
        info.MoreInfo.OtherNumber
    })
    .ToListAsync();

but none of those changed the join.但这些都没有改变加入。

I am able to get an inner join by using the join function, and not using the navigation property, but I would like to avoid this if possible.我可以通过使用 join 函数而不是使用导航属性来获得内部联接,但如果可能的话,我想避免这种情况。 The syntax is ugly and requires me to add MoreInfos to the context.语法很难看,需要我将 MoreInfos 添加到上下文中。 If it is possible, I would like to create an inner join using a navigation property.如果可能,我想使用导航属性创建内部联接。

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();

produces the correct SQL生成正确的 SQL

SELECT [info].[InfoId], [info].[Name], [moreInfo].[OtherName], [moreInfo].[OtherNumber]
FROM [Info] AS [info]
INNER JOIN [MoreInfo] AS [moreInfo] ON [info].[InfoId] = [moreInfo].[InfoId]

but I'd rather uses the navigation property.但我宁愿使用导航属性。

Currently (EF Core 2.1.2) it's not possible with the provided model.当前(EF Core 2.1.2)无法使用提供的模型。

The true one to one relationship technically cannot be enforced, so EF Core IsRequired denotes whether the dependent entity FK is nullable or not.真正的一对一关系在技术上无法强制执行,因此 EF Core IsRequired表示依赖实体 FK 是否可为空。 Which for the shared PK association like yours is always true.对于像您这样的共享 PK 协会来说,这始终是正确的。 Note that the Info is the principal while the MoreInfo is the dependent, hence MoreInfo requires Info , bit not vice versa, that's why EF Core generates left outer join when navigating from principal to dependent.请注意, Info是主体,而MoreInfo是从属,因此MoreInfo需要Info ,反之亦然,这就是 EF Core 在从主体导航到从属时生成左外连接的原因。

One thing you can do is to include not null criteria:您可以做的一件事是包含非空条件:

.Where(info => info.MoreInfo != null)

which when used in EF6 query (with some intermediate projection) was able to let the EF6 query translator use inner join.当在 EF6 查询(带有一些中间投影)中使用时,能够让 EF6 查询转换器使用内连接。 EF Core is still not smart enough, so all these tricks don't work. EF Core 仍然不够智能,因此所有这些技巧都不起作用。 It generates left outer join with right side key IS NOT NULL filter.它生成带有右侧键IS NOT NULL过滤器的左外连接。 Which is equivalent of a inner join and probably will be processed as such by the SQL query optimizer.这等效于内部联接,并且可能会由 SQL 查询优化器进行处理。 But still...但是还是...

The only way to get inner join currently (except manual join of course) is to navigate from the other end of the relationship, ie from dependent to required principal.当前获得内部联接的唯一方法(当然手动联接除外)是从关系的另一端导航,即从依赖关系到所需主体。 There is no need to expose DbSet<MoreInfo> from your context, the Set<TEntity>() method can be used to access any entity set.无需从上下文公开DbSet<MoreInfo>Set<TEntity>()方法可用于访问任何实体集。 You'd need an inverse navigation property though:不过,您需要一个反向导航属性:

public class MoreInfo
{
    // ...
    public virtual Info Info { get; set; }
}

and of course updated fluent configuration:当然还有更新的流畅配置:

modelBuilder.Entity<Info>()
    .HasOne(e => e.MoreInfo)
    .WithOne(e => e.Info)
    .HasForeignKey<MoreInfo>(e => e.InfoId);

Now the query can be rewritten as:现在查询可以改写为:

var x = await context.Set<MoreInfo>()
    .Select(moreInfo => new
    {
        moreInfo.Info.InfoId,
        moreInfo.Info.Name,
        moreInfo.OtherName,
        moreInfo.OtherNumber
    })
    .ToListAsync();

which will generate the desired inner join:这将生成所需的内部连接:

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