Извлечение коллекции внука из корневого родителя

#c# #linq #entity-framework #linq-to-entities

#c# #linq #entity-framework #linq-to-entities

Вопрос:

У меня есть следующий граф объектов:

Root (Root_Id)
—- Дочерний элемент (Child_Id, Root_Id)
—— Внук (GrandChild_Id, Child_Id)

И я хочу обойти дочернюю и вернуть коллекцию внуков, имеющую корневой объект. До сих пор я пробовал это:

 var child_Ids = db.Root
                .SingleOrDefault( r => r.Root_Id == rootID )
                .Childs
                .Select( ch => new {  Child_Id = ch.Child_Id} ).ToArray();

 return db.GrandChilds.Where( gc => child_Ids.Contains( gc.Child_Id ) );
 

Но это даже не будет компилироваться со следующими ошибками :
1) IEnumerable не содержит определения для Contains …
2) Экземпляр аргумента: не удается преобразовать из ‘AnonymousType # 1 []’ в ‘System.Linq.IQueryable

Как я могу это сделать?

Ответ №1:

              db.Root
            .SingleOrDefault( r => r.Root_Id == rootID )
            .Childs.SelectMany(ch=>ch.GrandChilds).Distinct()
 

Используйте .SelectMany расширение для получения коллекции внуков

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

1. В чем ошибка компиляции? Имя свойства может отличаться

Ответ №2:

Попробуйте это

 var child_Ids = db.Root
    .SingleOrDefault( r => r.Root_Id == rootID )
    .Childs
    .Select( ch => ch.Child_Id)
    .ToArray();

 return 
     from grandChild in db.GrandChild
         join child_id in child_Ids
         on child_id == grandChild.HandlingUnit_Id 
     select grandChild;
 

P.S: Я все еще немного не уверен в вашей цели, но это похоже на рабочее приближение вашего первоначального решения

Редактировать:

Если ваша иерархия и классы представляют собой что-то вроде:

 public class Db
{
    public Db(IEnumerable<Root> roots)
        {  this.Roots = new List<Root>(roots); }

    public ICollection<Root> Roots { get; private set; }
}

public class Root
{
    public Root(IEnumerable<Child> children )
    { 
        this.Children = new List<Child>(children);
    }

    public ICollection<Child> Children { get; private set; }
}


public class Child
{
    public Child(Int32 childId, Int32 rootId, IEnumerable<GrandChild> grandChildren)
    {
        this.Child_Id = childId;
        this.Root_Id = rootId; 
        this.GrandChildren = new List<GrandChild>(grandChildren);
    }

    public Int32 Child_Id { get; private set; }
    public Int32 Root_Id { get; private set; }
    public ICollection<GrandChild> GrandChildren {get; private set;}
}

public class GrandChild
{
    public GrandChild (Int32 grandChildId, Int32 childId)
    { 
        this.GrandChild_Id = grandChildId; 
        this.Child_Id = childId; 
    }

    public Int32 GrandChild_Id {get; private set;}
    public Int32 Child_Id {get; private set;}
}
 

Затем, как это уже было предложено AD.NET вы могли бы попробовать метод SelectMany

         GrandChild gc1 = new GrandChild(1, 10);
        GrandChild gc2 = new GrandChild(2, 10);
        GrandChild gc3 = new GrandChild(3, 11);

        Child c1 = new Child(10, 100, new GrandChild[]{ gc1, gc2 });
        Child c2 = new Child(11, 100, new GrandChild[]{ gc3 });

        Root r1 = new Root(new Child[]{c1, c2});

        Db db = new Db(new Root[] { r1 });

        var rootGrandChildren = db
            .Roots
            .FirstOrDefault()
            .Children
            .SelectMany(child => child.GrandChildren);
 

В синтаксисе запроса это будет выглядеть так

     var rootGrandChildren = from child in db.Roots.FirstOrDefault().Children
                            from grandChild in child.GrandChildren
                            select grandChild;
 

Но если ваш Child класс не знает своих внуков, и они (внуки) содержатся в Root:

 public class Child
{
    public Child(Int32 childId, Int32 rootId)
    {
        this.Child_Id = childId;
        this.Root_Id = rootId; 
    }

    public Int32 Child_Id { get; private set; }
    public Int32 Root_Id { get; private set; }
}

public class Root
{
    public Root(IEnumerable<Child> children, IEnumerable<GrandChild> grandChildren )
    { 
        this.Children = new List<Child>(children);
        this.GrandChildren = new List<GrandChild>(grandChildren );
    }

    public ICollection<Child> Children { get; private set; }
    public ICollection<GrandChild> GrandChildren{ get; private set; }
}
 

вам нужно будет использовать:

   Root r1 = new Root(new Child[]{c1, c2}, new GrandChild[]{gc1, gc2, gc3});

  Db db = new Db(new Root[] { r1 });


  Root root = db.Roots.FirstOrDefault();

  var rootGrandChildren = from child in root.Children
                              join grandChild in root.GrandChildren
                              on  child.Child_Id equals grandChild.Child_Id
                          select grandChild;
 

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

1. Цель состоит в том, чтобы получить коллекцию внуков, имеющих корневого родителя, мне не нужна коллекция дочерних элементов для моего варианта использования. Я попробую ваше предложение и дам вам знать.

2. Но что такое gc.HandlingUnit_Id в вашем исходном примере?

3. извините, должно быть, это была опечатка. (возвращает db. Внуки. Где( gc => child_Ids.Contains( gc.Child_Id ) );). Я обновил вопрос, чтобы отразить это