Как найти первого родителя в древовидной иерархии, используя LINQ с EF Core - PullRequest
2 голосов
/ 03 октября 2019

У меня есть две сущности:

public class Child
{
    [Key]
    public string Id { get; set; }
    public List<Parent> Parents { get; set; }
}

public class Parent
{
    [Key]
    public string Id { get; set; } // If the parent is in Child, the that Id, otherwise a random Guid.
    public string ParentId { get; set; }  // Id of the parent of this parent.
    public string ChildId { get; set; }   // Id of the child from the Child class.
    public Child Child { get; set; }
}

Контекст БД настроен так:

public class MyContext : DbContext
{
   public MyContext(DbContextOptions<MyContext> options) : base(options)
   {
   }

   protected override void OnModelCreating(ModelBuilder builder)
   {
       builder.Entity<Child>();
       builder.Entity<Parent>().HasKey(k => new {k.Id, k.ChildId});
       builder.Entity<Parent>()
              .HasOne(h => h.Child)
              .WithMany(w => w.Parents)
              .IsRequired()
              .OnDelete(DeleteBehavior.Cascade);
   }
}

Фактические данные таковы, что не каждый родитель находится в таблице Childно каждый ребенок находится в таблице Child. Я хочу запросить детей (из таблицы Child), которые оказались самыми старшими родителями в иерархии родитель-потомок. Например:

        /*
         * Tree:
         *     p1(P)--------p2 (P)------r2 (P&C)
         *      |-----------|           |
         *      |                       c3 (P&C)
         *      |                       |
         *      |                      gc1 (C)
         *     r1 (P&C)---|
         *      |         |
         *     c1 (C)     c2 (C)
         */

Это дерево должно давать r1 и r2 для решения, поскольку они находятся наверху дерева, а также в дочерней таблице. Другими словами, у них нет родителя, который также является ребенком.

До сих пор моя попытка:

using(var ctx = new MyContext(options))
{
    var q = ctx.Set<Child>();
    var mainQ = q.GroupJoin(q.Where(w => w.Parents.Any()),
                            o => o.Id,
                            i => i.Parents.First().ParentId /* For now I'm just trying to handle a single parent.*/,
                           (o, i) => new { o, i })
                 .Where(w => !w.i.Any())
                 .Select(s => s.o)
                 .Union(q.Where(w => !w.Parents.Any()));
}

Однако этот запрос выдает мне ошибку:

ArgumentException: входная последовательность должна иметь элементы типа Child, но она имеет элементы типа Microsoft.EntityFrameworkCore.Query.Internal.AnonymousObject

Ценить любую помощь.

1 Ответ

1 голос
/ 03 октября 2019

Мне только что удалось это выяснить:)

var childIds = ctx.Set<Child>().Select(s => s.Id);

var query = ctx.Set<Child>()
    .GroupJoin(ctx.Set<Parent>(), o => o.Id, i => i.Id, (o, i) => new {o, i})
    .Where(w => w.i.Any())
    .Select(s => s.o)
    .Include(i => i.Parents)
    .Where(w => !w.Parents.Select(s => s.Id).Intersect(childIds).Any());

Идея состоит в том, чтобы найти всех детей, являющихся родителями (GroupJoin с последующим предложением Where), а затем проверить, есть ли родители этих детейв таблице детей.

Если у кого-то есть лучшее / более эффективное решение, пожалуйста, оставьте ответ.

...