簡體   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