Загрузите объект, ссылающийся на себя, с помощью EF Core 5.0 (просто получите родителей и их дочерних элементов в их свойстве навигации)

#c# #entity-framework-core #ef-core-3.1 #self-reference #ef-core-5.0

#c# #entity-framework-core #ef-core-3.1 #самоссылка #ef-core-5.0

Вопрос:

Я хочу внедрить систему комментариев, это мой объект комментариев.

  public class Comment
    {
        public int CommentId { get; set; }
        public int? ParentId  { get; set; }
        public string Content { get; set; }
        public DateTimeOffset DateCreated { get; set; }
        public DateTimeOffset DateModified { get; set; }


        public Comment Parent { get; set; }
        public ICollection<Comment> Children { get; set; }


    }
  

и это мои конфигурации в Fluent API

 
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Comment>(comment =>
            {
                comment.HasKey(c => c.CommentId);
                comment.HasIndex(c => c.ParentId);

                comment.HasOne(c => c.Parent)
                       .WithMany(c => c.Children)
                       .HasForeignKey(c => c.ParentId);
            });
        }
  

все в порядке, и я могу загрузить все записи с иерархией (включая родительские и дочерние элементы) с помощью этого кода

   List<Comment> comments = await _db.Comments.Include(c => c.Children)
                .ToListAsync();
  

но этот код загружает все элементы, такие как дочерние элементы. Но я хочу загрузить всех родителей, а затем их дочерних элементов, а затем внуков и ….

Я использую этот код для этого сценария

 List<Comment> comments = await _db.Comments.Include(c => c.Children)
                .Where(c => c.ParentId == null)
                .ToListAsync();
  

и этот код просто загружает всех родителей со своими дочерними элементами, а не grand children и многое другое в иерархии.

Как мне написать этот запрос?

Комментарии:

1. Это очень сложный запрос, я рекомендую записать его в хранимую процедуру или табличную функцию на сервере и вместо этого запросить ее из EF. В EF нет понятия курсоров, и я полагаю, вы обнаружите, что они вам понадобятся. Или, по крайней мере, действительно точные запросы CTE.

2. Пожалуйста, ищите другие вопросы по иерархии LINQ или рекурсивному. Это постоянно повторяющаяся тема.

Ответ №1:

Я нашел решение для этого сценария.

   public async Task<IActionResult> Index()
        {

            List<Comment> comments = await _db.Comments.AsNoTrackingWithIdentityResolution()
                                                        .Include(c => c.Children)
                                                        .ToListAsync();

            // Structure Comments into a tree
            List<Comment> commentsTree = comments.Where(c => c.ParentId == null)
                                                 .AsParallel()
                                                 .ToList();

            return View(commentsTree);
        }