Home > Back-end >  entity framework core nested object with self parentId
entity framework core nested object with self parentId

Time:12-03

I have a treetable structure and this data comes to me from the frontend. In this treetable structure, there is IssueActivity and IssueActivityDetail for details of this issue. Now my question is, more than one IssueActivityDetail field can be added to this IssueActivity field. How can I do this on the c# ef core side? I tried to do it with the logic of ParentId. My Entity structures are as follows. I did not add the parentId in FluenApi because I did not fully understand it.

My IssueActivity table.


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

My IssueActivityDetail table.


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 Configuration.


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 is also the place where I try to receive and process the data, but I played a lot and couldn't do it correctly.

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

CodePudding user response:

You don't need to keep ParentId property in IssueActivityDetail.

public partial class IssueActivitiy
{
    ...
    public virtual List<IssueActivitiyDetail> IssueActivitiyDetails { get; set; }
}
public partial class IssueActivitiyDetail
{
    ...
    public virtual IssueActivitiy IssueActivity { get; set; }
}

Your configuration looks not wrong.

Maybe you can use Include when getting the entity from db context.

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

CodePudding user response:

You can accomplish this by retrieving all the entries from the database. Then select the Root node and then let EF Core mapping do the rest.

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

}

You could argue that ParentId == null would also imply that it's a parent node. this just makes the example given more tuitive imo.

You should consider performance, how many nodes will become an issue, is it exposed through a web-api and would iterating over the nodes be more efficient. So you wouldn't have to load the entire Tree into memory each time but let clients handle that instead.

  • Related