如何使用 EF 核心定义层次结构 table

How to define a hierarchal table using EF core

在我正在处理的应用程序中,我们有一个用户 table,TblUser。此 table 中的用户可能属于单个父用户。一个父用户可能有多个子用户。

这种关系在一个名为 TblUserMapping 的 table 中维护,它有两列,即 ParentUserId 和 ChildUserId,分别对应父项和子项的 TblUser.Id 值。 TblUser.Id 是一个自动递增的值。

我如何在 EF Core 中定义它,是否可以将 ChildUser 插入 TblUser 并使用自动生成的 Id 值来创建 TblUserMapping 记录?

现在我有:

[Table("TblUser")]
public class TblUser
{
    public TblUser()
    {        
        ChildUsers            = new List<TblUserMapping>();
    }

    public int Id { get; set; }
    public string UserName { get; set; }
    
    public virtual ICollection<TblUserMapping> ChildUsers { get; set; }
    public virtual TblUserMapping ParentUser { get; set; }
}

[Table("TblUserMapping")]
public class TblUserMapping
{
    public TblUserMapping()
    {
    }

    public int ChildUserId { get; set; }
    public int ParentUserId { get; set; }
    
    public virtual TblUser ChildUser { get; set; }
    public virtual TblUser ParentUser { get; set; }
}

public class TblUserMapping : IEntityTypeConfiguration<TblUser>
{
    public void Configure(EntityTypeBuilder<TblUser> entity)
    {
        entity.Property(e => e.Id).ValueGeneratedOnAdd();
        entity.Property(e => e.UserName)
            .IsRequired()
            .IsUnicode(false);
    }
}

public class TblUserMappingMapping : IEntityTypeConfiguration<Entities.TblUserMapping>
{
    public void Configure(EntityTypeBuilder<Entities.TblUserMapping> entity)
    {
        entity.HasKey(e => e.ChildUserId);
        entity.Property(e => e.ChildUserId)
            .IsRequired();
        entity.Property(e => e.ParentUserId)
            .IsRequired();
        
        
        entity.HasOne(e => e.ParentUser)
            .WithMany(e => e.ChildUsers)
            .HasForeignKey(e => e.ParentUserId);

        entity.HasOne(e => e.ChildUser)
            .WithOne(e => e.ParentUser)
            .HasForeignKey<TblUser>(e => e.Id);
    }
}

但这并没有像我希望的那样工作:

var userInformation = await _context
    .Users
    .Include(entity => entity.ChildUsers)
    .ThenInclude(entity => entity.ChildUser)
    .Where(s => s.UserName == userName)
    .FirstOrDefaultAsync();

var ChildUser = new TblUser
{
    UserName = userModel.UserName,
    ParentUser = new TblUserMapping()
    {
        ParentUser = userInfo
    }
};

_context.Users.Add(ChildUser);
await _context.SaveChangesAsync();

您可以附加导航属性,Entity Framework 会在创建 ID 时自动填充它们。您提供的示例 应该 有效,您可能需要向我们展示您是如何获得 userInfo 的,然后我们才能看到发生了什么。

也就是说,与其保留单独的映射 table,我会让每个子用户直接引用他们的父级:

[Table("TblUser")]
public class TblUser
{
    public int Id { get; set; }
    public string UserName { get; set; }

    public int? ParentId { get; set; }
    public TblUser Parent { get; set; }    

    // Lazy-loading is not enabled by default in EF Core, so you don't need the 'virtual' keyword
    // Also, if the initialization of a member does not depend on constructor arguments, I
    // prefer this syntax instead of doing it in the constructor
    public ICollection<TblUser> Children { get; set; } = new List<TblUser>();
}

您可以在模型中使用 InverseProperty 属性:

[Table("TblUser")]
public class TblUser
{
public TblUser()
{        
    ChildUsers            = new List<TblUserMapping>();
}

public int Id { get; set; }
public string UserName { get; set; }

[InverseProperty("ChildUser")]
public virtual ICollection<TblUserMapping> ChildUsers { get; set; }

[InverseProperty("ParentUser")]
public virtual TblUserMapping ParentUser { get; set; }
}

在其他型号中:

[Table("TblUserMapping")]
public class TblUserMapping
{
public TblUserMapping()
{
}

public int ChildUserId { get; set; }
public int ParentUserId { get; set; }

[ForeignKey("ChildUserId")]
public virtual TblUser ChildUser { get; set; }

[ForeignKey("ParentUserId")]
public virtual TblUser ParentUser { get; set; }
 }

如你所见,我用属性定义了这些关系,这意味着不需要在你的配置中出现。