Met één LINQ query Parent item en Child items ophalen

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • PdeBie
  • Registratie: Juni 2004
  • Laatst online: 08:25
Hoi allen,

ik heb een collectie van entiteiten in 1 tabel die onderling aan elkaar gelinkt zijn middels een parentId en een apart type hebben.
Een parent item kan meerdere childs hebben.

Nu wil ik eigenlijk met 1 LINQ query een parent item ophalen met alle child-items daaronder, alleen kom er niet echt uit.

De datastructuur is als volgt. Alle data komt dus uit 1 tabel!
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
    programme 
    {
        id: GUID
        type: tpEventProgramme
        rooms: [{
            id: GUID,
            parent-id: GUID Programme
            type: tpRoom
            Sessions : [{
                id: GUID,
                parent-id: GUID room
                type: tpSession
                Presentations: [{
                    id: GUID,
                    parent-id: GUID session
                    type: tpPresentation
                }]
            }]
        }]      
    }
}


Een programme heeft dus meerdere rooms, een room heeft meerdere sessions etc. etc.

In eerste instantie dacht ik dit op te kunnen lossen door de tabel meerdere malen aan zichzelf te joinen, maar dan krijg ik veel te veel resultaten terug, wat ook logisch is aangezien je alles aan elkaar joined. Eigenlijk zou ik 1 object terug willen krijgen.
Met een recursieve functie zou dit volgens mij mogelijk moeten zijn, maar ik kom er niet helemaal uit.

Iemand die mij een LINQ voorzetje kan geven?

Alle reacties


Acties:
  • 0 Henk 'm!

  • PdeBie
  • Registratie: Juni 2004
  • Laatst online: 08:25
Uiteraard ben ik zelf ook nog bezig en had bedacht om anonymous types te gebruiken:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var event = from p in events
            where p.type == types.programme
            select new
            {
                Programme = p,
                Rooms = from r in events
                        where r.type == types.room
                           && r.parentId == p.Id
                        select new
                        {
                            Room = r,
                            [...]
                        }

            }


Maar dit levert een error op. Zou een manier als dit werken als ik van die anonymous types echte klassen maak en daar de constructors van aanroep in de Select?

------
edit:

Bovenstaand zojuist geprobeerd en dit lijkt te werken. Het wordt echter wel echt een gedrocht van een query zo. Dit moet makkelijker kunnen lijkt me. Sowieso is het niet heel erg "DRY" op deze manier.

[ Voor 16% gewijzigd door PdeBie op 27-05-2016 15:42 ]


Acties:
  • +1 Henk 'm!

  • Daos
  • Registratie: Oktober 2004
  • Niet online
Is inderdaad niet zo mooi en ook is het traag met veel data. Volgens mij krijg je een nest van 3 queries en daarmee een complexiteit van O(N3). Het kan ook in O(N):

Men neme:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
class DBItem
{
    public int id;
    public int? parentID;
    public string name;
}

class TreeItem
{
    public int id;
    public List<TreeItem> children;
    public string name;
}

En dan doen we:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// some data
List<DBItem> Model = new List<DBItem> {
    new DBItem {id = 1, parentID = 5, name="Zuid-Holland"},
    new DBItem {id = 2, parentID = 8, name="Zaandam"},
    new DBItem {id = 3, parentID = 5, name="Utrecht"},
    new DBItem {id = 4, parentID = 1, name="Den Haag"},
    new DBItem {id = 5, parentID = null, name="Nederland"},
    new DBItem {id = 6, parentID = 1, name="Rotterdam"},
    new DBItem {id = 7, parentID = 8, name="Amsterdam"},
    new DBItem {id = 8, parentID = 5, name="Noord-Holland"},
};

// find root id  O(N)
var rootID = Model.Single(i => i.parentID == null).id;

// create (a lookup with) items  O(N)
var items = Model.ToDictionary(i => i.id, i => new TreeItem { id = i.id, name = i.name });

// gather children  O(N)
var children = Model.Where(i => i.parentID.HasValue).GroupBy(i => i.parentID.Value, i => items[i.id]);

// connect children to items  O(N)
foreach (var g in children)
{
    items[g.Key].children = g.ToList();
}

// get root  O(1)
var root = items[rootID];

Acties:
  • 0 Henk 'm!

  • PdeBie
  • Registratie: Juni 2004
  • Laatst online: 08:25
Ik zal er eens mee spelen met jouw oplossing. Enige waar ik nog even mee zit in jouw query is met de kolom 'type'. Die verschilt per 'laag'. Maar daar is vast ook een mouw aan te knopen.