實體框架核心兩個外鍵 - 相同的表

c# entity-framework entity-framework-core

我遇到了對同一個表有兩個外鍵引用的問題。填充了外鍵id字段,但導航字段和列表(Team字段)不是 - 它們都是null。

我的課程是:

public class Team
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Fixture> HomeFixtures { get; set; }
    public virtual ICollection<Fixture> AwayFixtures { get; set; }
}

public class Fixture
{
    public int Id { get; set; }

    public int HomeTeamId { get; set; }
    public int AwayTeamId { get; set; }

    public Team HomeTeam { get; set; }
    public Team AwayTeam { get; set; }
}

和我的dbContext

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public DbSet<Team> Teams { get; set; }
    public DbSet<Fixture> Fixtures { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Fixture>()
            .HasOne(f => f.HomeTeam)
            .WithMany(t => t.HomeFixtures)
            .HasForeignKey(t => t.HomeTeamId)
            .OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);

        modelBuilder.Entity<Fixture>()
            .HasOne(f => f.AwayTeam)
            .WithMany(t => t.AwayFixtures)
            .HasForeignKey(t => t.AwayTeamId)
            .OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
    }
}

我已經嘗試將[ForeignKey()]屬性添加到HomeTeam和AwayTeam屬性但它沒有任何效果。我也試過改變OnModelCreating方法以另一種方式工作,即

modelBuilder.Entity<Team>()
    .HasMany(t => t.HomeFixtures)
    .WithOne(f => f.HomeTeam)
    .HasForeignKey(f => f.HomeTeamId)
    .OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);

對於遠程固定裝置也是如此,但這會產生相同的行為。

我的查詢方式似乎並不重要,但最簡單的情況是

Fixture fixture = await _context.Fixtures.SingleOrDefaultAsync(f => f.Id == id);

返回的fixture對象包含有效且在數據庫中但仍未填充Team對象的團隊ID。

有誰知道我做錯了什麼?這是一個全新的項目和一個全新的數據庫,因此沒有遺留代碼干擾。我正在使用Visual Studio 2017rc和Entity Framework Core。

一般承認的答案

目前EF Core不支持延遲加載。跟踪問題在這裡

這意味著默認情況下不會加載導航屬性並保持為null。作為解決方法,您可以使用預先加載或顯式加載模式。

渴望加載

Eager loading是一種模式,您可以在使用Include API運行查詢時急切地請求引用的數據。其用法與EF6中的工作方式略有不同。要包含任何導航,請在查詢中的include方法中指定lambda表達式(或字符串名稱)。

例如await _context.Fixtures.Include(f => f.HomeTeam).FirstOrDefaultAsync(f => f.Id == id);

目前,不支持過濾包含,因此您可以請求完全加載導航或排除導航。所以lambda表達式不能複雜。它必須是簡單的屬性訪問。另外,要加載嵌套導航,您可以使用屬性訪問調用(如abc )鏈接它們,或者在收集後導航(因為您無法鏈接它們)時使用ThenInclude

例如await _context.Fixtures.Include(f => f.HomeTeam).ThenInclude(t=> t.HomeFixtures).FirstOrDefaultAsync(f => f.Id == id);

值得記住的是,include表示從被調用的實體類型導航的路徑,以填充路徑上的所有naviagations。如果您在第二級或更高級別包含多個導航,通常可能需要編寫重複的呼叫。這僅僅是為了語法,查詢將被優化,不會重複工作。此外,使用字符串include,您可以指定整個導航路徑,而無需使用ThenInclude。由於參考導航可以利用連接來獲取單個查詢中所需的所有數據,因此收集導航可以在單個查詢中加載所有相關數據,急切加載是最高效的方式。

明確加載

當您在內存中加載對象並需要加載導航時,延遲加載會在訪問導航屬性時加載它,如果沒有,您需要自己調用Load方法。這些方法在ReferenceEntryCollectionEntry上定義。

例如

Fixture fixture = await _context.Fixtures.SingleOrDefaultAsync(f => f.Id == id);
_context.Entry(fixture).Reference(f => f.HomeTeam).Load();

var team = await _context.Teams.SingleOrDefaultAsync(t => t.Id == id);
_context.Entry(team).Collection(f => f.HomeFixtures).Load();

對於參考導航則需要ReferenceEntityEntry得到ReferenceEntry。對於集合導航,等效方法是Collection 。然後,您只需在其上調用Load方法即可在導航中加載數據。如果需要, LoadAsync異步版本。

希望這可以幫助。



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow