如何在 EF Core 中使用连接 table 映射具有 "self-hierarchical" 关系的实体

How to map entity with "self-hierarchical" relationship using a join table in EF Core

我在使用现有数据库结构时遇到了一些问题,遗憾的是我无法更改。

我有以下 tables(为简单起见缩写)

CREATE TABLE EntityRelationship (
    ChildID int,
    ParentID int
)

CREATE TABLE Entity (
    EntityID varchar,
    EntityRelationshipID int
)

我将实体映射如下:

public class EntityRelationship {
    [ForeignKey("Entity")]
    public int ChildId { get; set; }
    [ForeignKey("ParentEntity")]
    public int ParentId { get; set; }

    public Entity ParentEntity { get; set; }
    public Entity Entity { get; set; }
}

public class Entity {
    [Key]
    public String EntityId { get; set; }

    [ForeignKey("ParentEntity")]
    public int EntityRelationshipId { get; set; }

    public EntityRelationship ParentEntity { get; set; }
}

modelBuilder.Entity<EntityRelationship>()
    .HasOne(c => c.ChildEntity)
    .WithOne(c => c.ParentEntity);

我确保在加载时预先加载导航属性,但我仍然没有得到任何结果。

我假设它与我的映射有关,或者可能是因为带有连接 table 的数据库结构不是按照惯例使用一对一映射,它会触发整件事。

任何人都可以阐明这一点吗?

为了清楚起见,我希望能够做的是:

Entity parent = db.Entities.First().ParentEntity.Entity;

while (parent.ParentEntity.Entity != null) {
     parent = parent.ParentEntity.Entity;
}

以便找到实体的最顶层父级。

使用此模型,link 实体 EntityRelationship 暗示 两个 FK 关系由 ChildIdParentId 表示。对于这两种关系,link 实体是 dependent,主要实体 Entityprincipal with EntityRelationshipId作为主密钥

所以基本上你需要正确映射这些两个关系的键和导航属性。删除 [ForeignKey] 数据注释以免乱七八糟

public class Entity
{
    [Key]
    public string EntityId { get; set; }
    public int EntityRelationshipId { get; set; }
    public EntityRelationship ParentEntity { get; set; }
}

public class EntityRelationship
{
    public int ChildId { get; set; }
    public int ParentId { get; set; }
    public Entity ParentEntity { get; set; }
    public Entity Entity { get; set; }
}

然后使用以下流畅的配置(它可以从另一侧配置,我选择 Entity 因为它看起来更自然地看到映射):

modelBuilder.Entity<Entity>(builder =>
{
    // child => parent
    builder.HasOne(e => e.ParentEntity)
        .WithOne(r => r.Entity)
        .HasForeignKey<EntityRelationship>(r => r.ChildId)
        .HasPrincipalKey<Entity>(e => e.EntityRelationshipId);
    // parent => children
    builder.HasMany<EntityRelationship>()
        .WithOne(r => r.ParentEntity)
        .HasForeignKey(r => r.ParentId)
        .HasPrincipalKey(e => e.EntityRelationshipId);
});