具有自身 parentId 的实体框架核心嵌套对象

Posted

技术标签:

【中文标题】具有自身 parentId 的实体框架核心嵌套对象【英文标题】:entity framework core nested object with self parentId 【发布时间】:2022-01-08 20:11:43 【问题描述】:

我有一个树表结构,这些数据来自前端。 在这个树形表结构中,有 IssueActivity 和 IssueActivityDetail 来了解这个问题的细节。 现在我的问题是,这个 IssueActivity 字段可以添加多个 IssueActivityDetail 字段。如何在 c# ef 核心方面做到这一点? 我试图用 ParentId 的逻辑来做。我的实体结构如下。 FluenApi中没有添加parentId,因为我没有完全理解。

我的 IssueActivity 表。


public partial class IssueActivitiy

public int Id  get; set; 
public int IssueId  get; set; 
public byte Type  get; set; 
public short SubActivityNo  get; set; 
public string SubActivityTitle  get; set; 
public virtual Issue Issue  get; set; 
public virtual List<IssueActivitiyDetail> IssueActivitiyDetails  get; set; 


我的 IssueActivityDetail 表。


public partial class IssueActivitiyDetail

public int Id  get; set; 
public int IssueActivityId  get; set; 
public short LineNo  get; set; 
public string Definition  get; set; 
public byte RoleId  get; set; 
public byte Medium  get; set; 
public string Explanation  get; set; 
public int? ParentId  get; set; 
public virtual IssueActivitiy IssueActivity  get; set; 

FluentApi 配置。


public void Configure(EntityTypeBuilder<IssueActivitiy> modelBuilder)

modelBuilder.ToTable("IssueActivitiy");
modelBuilder.HasKey(a => a.Id);
modelBuilder.Property(e => e.SubActivityNo).HasComment("Sıra No");
modelBuilder.Property(e => e.SubActivityTitle).HasMaxLength(256).IsUnicode(false);
modelBuilder.Property(e => e.Type).HasDefaultValueSql("((1))").HasComment("1) Temel Aktivite\r\n2) Alternatif Aktivite\r\n3) İşlem İptal Aktivite");
modelBuilder.HasOne(d => d.Issue).WithMany(p => p.IssueActivitiys).HasForeignKey(d => d.IssueId).OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FK_Issue_IssueActivitiy_Id");

public void Configure(EntityTypeBuilder<IssueActivitiyDetail> modelBuilder)

modelBuilder.ToTable("IssueActivitiyDetail");
modelBuilder.Property(e => e.Definition).IsRequired().HasMaxLength(2048).IsUnicode(false).HasComment("Açıklama");
modelBuilder.Property(e => e.Explanation).HasMaxLength(2048).IsUnicode(false).HasComment("Açıklama");
modelBuilder.Property(e => e.IssueActivityId).HasComment("Konu Id");
modelBuilder.Property(e => e.LineNo).HasComment("Sıra No");
modelBuilder.Property(e => e.Medium).HasComment("Ortam (Excel, Mail vb.)");
modelBuilder.Property(e => e.RoleId).HasComment("Rol");
modelBuilder.Property(e => e.ParentId);
modelBuilder.HasOne(d => d.IssueActivity).WithMany(p => p.IssueActivitiyDetails).HasForeignKey(d => d.IssueActivityId).OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FK_IssueActivitiy_IssueActivitiyDetail_");

Web Api也是我尝试接收和处理数据的地方,但是玩了很多,都做不好。

var vIssueActivity = issueInfo.IssueActivitiyInfos
.Select(a => new IssueActivitiy

Type = a.Type,
SubActivityNo = a.SubActivityNo,
SubActivityTitle = a.SubActivityTitle,
IssueActivitiyDetails = a.IssueActivitiyDetailInfos
.Select(x => new IssueActivitiyDetail

 
LineNo = x.LineNo,
Definition = x.Definition,
RoleId = vUser.RoleId,
Medium = x.Medium,
Explanation = x.Explanation,
IssueActivityDetail = new List<IssueActivitiyDetail>  
).ToList()
);

【问题讨论】:

【参考方案1】:

您无需将ParentId 属性保留在IssueActivityDetail 中。

public partial class IssueActivitiy

    ...
    public virtual List<IssueActivitiyDetail> IssueActivitiyDetails  get; set; 

public partial class IssueActivitiyDetail

    ...
    public virtual IssueActivitiy IssueActivity  get; set; 

您的配置看起来没有错。

也许你可以在从数据库上下文中获取实体时使用Include

var issueActivity = context.IssueActivities.Include(x => x.IssueActivityDetails).FirstOrDefault();

【讨论】:

【参考方案2】:

您可以通过从数据库中检索所有条目来完成此操作。然后选择 Root 节点,然后让 EF Core 映射完成剩下的工作。

public class TreeNode 

    public bool IsRoot  get; set; 
    public int? ParentNodeId get; set;
    public virtual List<TreeNode> ChildNodes get; set;


public class TreeNodeRepository 


  public async Task<TreeNode> GetTreeStructure()
  
      var allNodes = await _context.TreeNodes.ToListAsync();

      return allNodes.FirstOrDefault(t => t.IsRoot);
  


您可以争辩说ParentId == null 也暗示它是一个父节点。这只是让 imo 给出的示例更加直观。

您应该考虑性能,有多少节点会成为一个问题,它是否通过 web-api 公开并且迭代节点会更有效。因此,您不必每次都将整个 Tree 加载到内存中,而是让客户端来处理。

【讨论】:

以上是关于具有自身 parentId 的实体框架核心嵌套对象的主要内容,如果未能解决你的问题,请参考以下文章

核心数据 - 将实体保存为 pList / 字典

如何过滤嵌套集合实体框架对象?

如何将具有嵌套属性的 JSON 对象反序列化为 Symfony 实体?

如何实现递归自连接实体框架?

同一列上具有多个外键的实体框架核心

如何使用实体框架进行递归加载?