C# EF Core 实现带ParentId自引用的IssueActivity多明细嵌套保存
实现方案
1. 补充IssueActivityDetail实体的自引用导航属性
你的现有实体只定义了ParentId字段,缺少父子层级的导航属性,需要补充:
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; } // 新增自引用导航属性 public virtual IssueActivitiyDetail? Parent { get; set; } public virtual List<IssueActivitiyDetail> Children { get; set; } = new(); }
注:你当前定义的实体名IssueActivitiy存在拼写错误(正确拼写为IssueActivity),建议统一修正避免后续维护问题
2. 修正Fluent API自引用配置
在IssueActivitiyDetail的Configure方法中补充自引用关联配置,注意关闭级联删除避免循环引用异常:
public void Configure(EntityTypeBuilder<IssueActivitiyDetail> modelBuilder) { modelBuilder.ToTable("IssueActivitiyDetail"); modelBuilder.HasKey(e => e.Id); // 补充遗漏的主键配置 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.HasOne<IssueActivitiyDetail>(e => e.Parent) .WithMany(e => e.Children) .HasForeignKey(e => e.ParentId) .OnDelete(DeleteBehavior.Restrict); // 禁止级联删除,避免循环引用报错 modelBuilder.HasOne(d => d.IssueActivity).WithMany(p => p.IssueActivitiyDetails).HasForeignKey(d => d.IssueActivityId).OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FK_IssueActivitiy_IssueActivitiyDetail_"); }
3. 修正树形数据转换逻辑
你原来的代码没有递归处理子层级,需要新增递归转换方法,将前端传的树形结构正确映射为EF的实体层级:
// 新增子节点递归转换方法 private IssueActivitiyDetail ConvertDetail(IssueActivitiyDetailInfo input, int roleId) { var detail = new IssueActivitiyDetail { LineNo = input.LineNo, Definition = input.Definition, RoleId = roleId, Medium = input.Medium, Explanation = input.Explanation }; // 递归处理当前节点的所有子节点 if (input.IssueActivitiyDetailInfos?.Any() == true) { foreach (var child in input.IssueActivitiyDetailInfos) { detail.Children.Add(ConvertDetail(child, roleId)); } } return detail; } // 外层转换逻辑 var vIssueActivities = issueInfo.IssueActivitiyInfos .Select(a => new IssueActivitiy { Type = a.Type, SubActivityNo = a.SubActivityNo, SubActivityTitle = a.SubActivityTitle, IssueActivitiyDetails = a.IssueActivitiyDetailInfos .Select(x => ConvertDetail(x, vUser.RoleId)) .ToList() }).ToList();
4. 数据保存
直接将转换后的实体加入DbContext执行保存即可,EF Core会自动处理层级关联的外键填充(包括IssueActivityId和ParentId),不需要手动赋值:
dbContext.IssueActivitiys.AddRange(vIssueActivities); await dbContext.SaveChangesAsync();
内容的提问来源于stack exchange,提问作者Celal Poyraz
相关产品推荐
相关产品推荐

