2013-04-26 13 views
5

Sto proiettando LINQ ai risultati SQL in classi fortemente tipizzate: padre e figlio. La differenza di prestazioni tra queste due domande è di grandi dimensioni:query LINQ proiettata uno a molti viene eseguita ripetutamente

query lente - la registrazione dal DataContext mostra che una chiamata separata al DB è stato fatto per ciascun genitore

var q = from p in parenttable 
     select new Parent() 
     { 
      id = p.id, 
      Children = (from c in childtable 
         where c.parentid = p.id 
         select c).ToList() 
     } 
return q.ToList() //SLOW 

veloce Query - accedendo dagli show DataContext una singola query colpo db che restituisce tutti i dati richiesti

var q = from p in parenttable 
     select new Parent() 
     { 
      id = p.id, 
      Children = from c in childtable 
         where c.parentid = p.id 
         select c 
     } 
return q.ToList() //FAST 

voglio forzare LINQ to utilizzare lo stile unico di query del secondo esempio, ma popolare le classi genitore con i propri figli oggetti direttamente. in caso contrario, la proprietà Children è una IQuerierable<Child> che deve essere interrogata per esporre l'oggetto Child.

Le domande di riferimento non sembrano affrontare la mia situazione. l'uso di db.LoadOptions non funziona. forse richiede che il tipo sia un TEntity registrato con DataContext.

DataLoadOptions options = new DataLoadOptions(); 
    options.LoadWith<Parent>(p => p.Children); 
    db.LoadOptions = options; 

Si prega di notare: genitori e bambini sono tipi semplici, non Table<TEntity> tipi. e non esiste una relazione contestuale tra genitore e figlio. le sottoquery sono ad-hoc.

The Crux of the Issue: nel secondo esempio LINQ attuo le istruzioni IQueriable e non chiamo la funzione ToList() e per qualche motivo LINQ sa come generare una singola query in grado di recuperare tutti i dati richiesti. Come faccio a popolare la mia proiezione ad-hoc con i dati effettivi come si ottiene nella prima query? Inoltre, se qualcuno potesse aiutarmi a esprimere meglio la mia domanda, lo apprezzerei.

+0

Come "Bambino" può essere un "tipo semplice"? Dovrebbe essere un tipo mappato. Ed è 'parenttable' a' Table' o il risultato di una query linq precedente? Alcune istruzioni di linq possono far sì che L2S passi da unirsi a N + 1. –

+1

Marcatori duplicati: conosci la differenza tra entity framework e linq in sql? –

+0

@GertArnold Mentre la parte LINQ di EF/L2S spesso fa duplicare funzionalmente domande simili, in * questo * caso credo che tu sia del tutto corretto; non sono duplicati. Riapertura. –

risposta

0

È necessario impostare le opzioni corrette per il caricamento dei dati.

options.LoadWith<Document>(d => d.Metadata); 

Guarda this

P.S. Include solo per LINQToEntity.

+0

Sembra che dovrebbe essere la risposta, ma non ha funzionato per me. ho provato dlo.LoadWith (p => p.Children). ha ancora più db hits – Paul

+0

@Paul Non è necessario utilizzare parenttable. Hai generato l'entità Parent di LINQ2SQL? Quindi la tua entità contiene già Bambini quando richiedi un Parent dal contesto. –

0

La seconda query è veloce proprio perché I bambini non vengono popolati.

E il primo è lento solo perché I bambini vengono popolati.

scegliere quello che si adatta alle vostre esigenze, semplicemente non può avere le loro caratteristiche insieme!

EDIT:

Come @Servy dice:

Nella vostra seconda query non sono in realtà recuperano tutte le informazioni sui bambini. Hai creato le query, ma non si è effettivamente eseguito per ottenere i risultati di tali ricerche. Se si dovesse scorrere l'elenco, quindi scorrere la raccolta dei bambini di ogni elemento che ci si vede prendendo tutto il tempo che la prima query.

1

Il modo più veloce che ho trovato per realizzare questo è di fare una query che restituisce tutti i risultati quindi raggruppare tutti i risultati. Assicurati di fare una .ToList() sulla prima query, in modo che la seconda query non faccia molte chiamate.

Qui r dovrebbe avere ciò che si desidera ottenere con una sola query db.

  var q = from p in parenttable 
        join c in childtable on p.id equals c.parentid 
        select c).ToList(); 
      var r = q.GroupBy(x => x.parentid).Select(x => new { id = x.Key, Children=x }); 
+1

Invece di fare un join e poi un gruppo, basta usare un GroupJoin. – Servy

5

E 'importante ricordare che le query LINQ si affidano a esecuzione differita. Nella tua seconda query non stai recuperando alcuna informazione sui bambini. Hai creato le query, ma non si è effettivamente eseguito per ottenere i risultati di tali ricerche. Se si dovesse iterare l'elenco e quindi iterare la raccolta Children di ciascun elemento, si vedrebbe che impiega tutto il tempo della prima query.

La query è anche intrinsecamente molto inefficiente. Stai utilizzando una query nidificata per rappresentare una relazione Join. Se si utilizza uno Join, la query potrà essere ottimizzata in modo appropriato sia dal provider di query sia dal database per eseguire molto più rapidamente. Potrebbe inoltre essere necessario regolare gli indici nel database per migliorare le prestazioni. Ecco come potrebbe apparire il join:

var q = from p in parenttable 
     join child in childtable 
     on p.id equals child.parentid into children 
     select new Parent() 
     { 
      id = p.id, 
      Children = children.ToList(), 
     } 
return q.ToList() //SLOW 
+0

So che la domanda riguarda LINQ to SQL, ma ho pensato di menzionare che se si utilizza Entity Framework, 'ToList()' non è richiesto su 'children'. –

Problemi correlati