c# - Entity Framework Core 两个外键 - 同一张表

标签 c# entity-framework entity-framework-core

我在对同一个表有两个外键引用时遇到问题。 外键 ID 字段已填充,但导航字段和列表(团队字段)未填充 - 它们均为空。

我的类(class)是:

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

返回的夹具对象包含有效且在数据库中的团队 ID,但团队对象仍未填充。

有人知道我做错了什么吗? 这是一个全新的项目和一个全新的数据库,因此没有遗留代码干扰。 我将 Visual Studio 2017rc 与 Entity Framework Core 结合使用。

最佳答案

目前 EF Core 不支持延迟加载。跟踪问题here

这意味着默认情况下导航属性不会被加载并且将保持为空。作为变通方法,您可以使用预先加载或显式加载模式。

预加载

急切加载是一种模式,您可以在使用 Include API 运行查询时急切地请求所需的引用数据。该用法与它在 EF6 中的工作方式有些不同。要包含任何导航,您可以在查询的 include 方法中指定 lambda 表达式(或字符串名称)。

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

目前不支持过滤包含,因此您可以请求完全加载导航或排除它。所以 lambda 表达式不能很复杂。它必须是简单的属性访问。此外,要加载嵌套导航,您可以使用属性访问调用(如 a.b.c)链接它们,或者在其后集合导航(因为您无法链接它们)时使用 ThenInclude

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

最好记住,include 表示来自实体类型的导航路径,它被调用以填充路径上的所有导航。如果您在第 2 级或更高级别包含多个导航,通常您可能需要编写重复调用。这只是语法,查询将被优化并且不会重复工作。此外,使用字符串包含,您可以指定整个导航路径,而无需使用 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();

对于引用导航,您需要EntityEntry 上的Reference 来获取ReferenceEntry。对于集合导航,等效方法是 Collection。然后你只需调用它的 Load 方法来加载导航中的数据。如果需要,还有 LoadAsync 的异步版本。

希望这对您有所帮助。

关于c# - Entity Framework Core 两个外键 - 同一张表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42286533/

相关文章:

entity-framework - Entity Framework 代码优先和 View 映射

c# - Entity Framework 7 : when db. 需要 Entity.Update(x)?

c# - 如何等待 ThreadState.Abort?

c# - 如何使用 c# winform 将 10,000 行数据从存储过程移动到 excel

c# - Entity Framework 类型初始值设定项异常

c# - 具有 MVC6 依赖项注入(inject)的 EF7 构造函数

c# - 将属性访问表达式转换为谓词表达式

c# - "String was not recognized as a valid DateTime."Window 7电脑环境出现错误

c# - 获取特定日期范围内的记录

c# - Redis AOF-Persistence 设置为 'always' 可以比 'every sec' 有更好的性能吗?