TLDR : Vorrei sapere se è possibile utilizzare diversi "include logiche" per un tipo di entità in una singola query possibile nel core EF.
Modifica : Giusto per essere chiari, nel titolo ho detto tenere traccia delle entità perché penso che sia quello che fa EF, ma .AsNoTracking()
non fa nulla di utile qui, prima che qualcuno suggerisca.
Il problema attuale è su un'applicazione React relativamente piccola supportata da un'applicazione API web ASP.NET Core. Quello che voglio realizzare è, quando chiamo api/parents
, voglio che l'applicazione mi dia un json che assomigli a questo:
[
{
"id": "parentid1",
"extraProperty": "value",
"children": [
{
"id": "childid1",
"parent": {
"id": "parentid1",
"extraProperty": "value"
}
}
]
}
]
La mia configurazione è simile alla seguente:
Query EF :
(from p in _context.Parents
select p)
.Include(p => p.Children)
.ThenInclude(c => c.Parent)
.ToList();
Dopo di che ho AutoMapper che associa l'entità al dto, non c'è molto da fare lì. Uso anche Json Serializer predefinito (Newtonsoft) per l'applicazione. È configurato con SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore
.
Con questa configurazione la chiamata API restituisce questa risposta:
[
{
"id": "parentid1",
"extraProperty": "value",
"children": [
{
"id": "childid1"
}
]
}
]
Che come puoi vedere "ignora" l'auto-riferimento del genitore.
La soluzione che mi è venuta in mente è stata quella di configurare Newtonsoft per "serializzare" i loop di riferimento e l'ho provato. E genera, perché la query EF fornita sopra restituisce un elenco di entità simile al seguente:
[
{
"id": "parentid1",
"extraProperty": "value",
"children": [
{
"id": "childid1",
"parent": {
"id": "parentid1",
"extraProperty": "value",
"children": [
{
"id": "childid1",
"parent": {
"id": "parentid1",
"extraProperty": "value"
...
}
}
]
}
}
]
}
]
Se guardi le mie chiamate Includi, dice esplicitamente che per i miei oggetti figlio voglio solo l'oggetto genitore e nessun altro riferimento all'interno di quell'oggetto genitore.
A mio .Include(child).ThenInclude(parent)
, EF utilizza l'impostazione iniziale .Include(child).ThenInclude(parent)
quando incontra un oggetto Parent per quella query. Quindi, quando trova un oggetto Parent
nell'oggetto Child
, invece di usare no include (che non ho più dopo ThenInclude
) usa .Include(child).ThenInclude(parent)
.
Non voglio risolvere questo problema attraverso hack con il mapper o il serializzatore, se non è necessario. Vorrei sapere se ciò che sto cercando è possibile: utilizzare diversi "include logiche" per un tipo di entità in una singola query.
La ThenInclude(parent)
è ridondante poiché si inizia con il genitore: i figli materializzati avranno già la loro proprietà Parent
popolata. Dovresti personalizzare la serializzazione come indicato da @IvanStoev nel primo commento.
L'alternativa è eseguire una query per i figli per genitore, quindi proiettare il risultato desiderato:
var parents = _context.Children
.Include( c => c.Parent )
.GroupBy( c => c.Parent )
.ToArray()
.Select( g =>
{
// assign children to g.Key (the parent object)
g.Key.Children = g.ToArray(); // I don't know type of `Children` property
// select parent
return g.Key;
} );