2011-12-21 10 views
10

Ho letto this le risposte alle domande che spiegano l'ordine del LINQ ai metodi degli oggetti fa la differenza. La mia domanda è perché?Perché l'ordine dei metodi LINQ per gli oggetti conta

Se scrivo una query LINQ to SQL, non importa l'ordine del LINQ metodi- projections ad esempio:

session.Query<Person>().OrderBy(x => x.Id) 
         .Where(x => x.Name == "gdoron") 
         .ToList(); 

L'albero espressione verrà trasformato in uno SQL razionale come questo:

SELECT * 
    FROM  Persons 
    WHERE Name = 'gdoron' 
    ORDER BY Id; 

Quando Eseguo la query, la query SQL verrà creata in base all'albero dell'espressione, indipendentemente dall'ordine dei metodi.
Perché non funziona allo stesso modo con LINQ to objects?
quando si enumera uno IQueryable tutte le proiezioni possono essere inserite in un ordine razionale (ad esempio Ordina per dopo Dove) esattamente come fa l'ottimizzatore di base dati.

+0

Molto vicino, ha buone risposte: [fa-la-fine-di-LINQ funzioni-materia] (http://stackoverflow.com/questions/7499384/does-the-order-of -linq-functions-matter) – nawfal

risposta

14

Perché non funziona in questo modo con LINQ to Objects?

LINQ to Objects non usa alberi di espressione. L'istruzione viene trasformata direttamente in una serie di chiamate al metodo, ognuna delle quali viene eseguita come un normale metodo C#.

Come tale, quanto segue in LINQ to Objects:

var results = collection.OrderBy(x => x.Id) 
        .Where(x => x.Name == "gdoron") 
        .ToList(); 

viene trasformato in metodo diretto chiamate:

var results = Enumerable.ToList(
        Enumerable.Where(
        Enumerable.OrderBy(collection, x => x.Id), 
        x => x.Name = "gdoron" 
        ) 
       ); 

Osservando le chiamate di metodo, si può capire perché ordinare le cose. In questo caso, posizionando OrderBy per primo, lo si annida efficacemente nella chiamata al metodo più interna. Ciò significa che l'intera raccolta verrà ordinata quando vengono elencati i resout. Se si dovesse cambiare l'ordine:

var results = collection 
        .Where(x => x.Name == "gdoron") 
        .OrderBy(x => x.Id) 
        .ToList(); 

Poi i risultanti interruttori metodo catena a:

var results = Enumerable.ToList(
        Enumerable.OrderBy(
        Enumerable.Where(collection, x => x.Name = "gdoron"), 
        x => x.Id 
        ) 
       ); 

Questo, a sua volta, significa che solo i risultati filtrati dovranno essere filtrate come esegue OrdinaPer.

+0

Grazie! Quindi, perché LINQ per gli oggetti non utilizza l'albero delle espressioni per migliorare le prestazioni? – gdoron

+0

@ gdoron Perché migliorerebbe le prestazioni? –

+0

@EtiennedeMartel, (ordinare, filtrare) => (filtraggio, ordinamento) == Incantesimo prestazioni ... – gdoron

8

L'esecuzione differita di Linq per oggetti funziona diversamente da linq-to-sql (e EF's).

Con linq-to-objects, la catena di metodi verrà eseguita nell'ordine in cui i metodi sono elencati, non utilizza gli alberi di espressione per archiviare e tradurre l'intera operazione.

Calling OrderBypoiWhere con LINQ to Objects, quando si enumera i risultati, ordinare la raccolta, poi filtrare. Al contrario, filtrare i risultati con una chiamata a Whereprima di ordinandolo con OrderBy, quando si enumera, prima filtrare, quindi ordinare. Di conseguenza, il secondo caso può fare una grande differenza, dal momento che potresti potenzialmente ordinare molti meno articoli.

+1

Linq agli oggetti utilizza l'esecuzione posticipata ... Finché non si enumerano i risultati, neanche l'elaborazione si verifica in Linq su Oggetti. Semplicemente non utilizza gli alberi di espressione per convertire l'intera catena di metodi in un'unica operazione, ma esegue direttamente i metodi, ognuno dei quali restituisce un'enumerabile che viene effettivamente eseguita come enumerata. –

+0

@Reed - grazie. Lo sapevo - non sono sicuro del perché stavo pensando così stupidamente. Ho fatto +1 a rispondere e correggere il mio. –

+1

È importante sapere - questo fa parte del perché le persone così spesso (anche se non sempre è buono) aggiungono .ToList() - forza un'enumerazione, che fa eseguire immediatamente l'intera operazione. Tuttavia, è possibile testarlo facilmente: basta scrivere collection.OrderBy (...) su una grande raccolta e ignorare i risultati. Scoprirai che non ha quasi nessun costo di esecuzione. –

4

Perché, con LINQ per SQL, la grammatica SQL per SELECT richiede che le diverse clausole si verifichino in una particolare sequenza. Il compilatore deve generare SQL grammaticalmente corretto.

L'applicazione di LINQ per oggetti su un oggetto IEnumerable implica l'iterazione su IEnumerable e l'applicazione di una sequenza di azioni a ciascun oggetto in IEnumerable. L'ordine conta: alcune azioni possono trasformare l'oggetto (o il flusso di oggetti stesso), altri possono buttare via oggetti (o iniettare nuovi oggetti nel flusso).

Il compilatore non può interpretare il tuo intento. Costruisce codice che fa ciò che hai detto di fare nell'ordine in cui hai detto di farlo.

1

Linq to Objects non riordina per evitare una fase di runtime presunta per eseguire un'operazione che dovrebbe essere ottimizzata in fase di codifica. I ricondizionatori del mondo potrebbero a un certo punto introdurre strumenti di analisi del codice per eliminare opportunità di ottimizzazione come questa, ma non è sicuramente un lavoro per il runtime.

+2

Questo in realtà non è vero. Mentre esiste un modello concettuale di come viene eseguita una query SELECT (ad es.calcolare il prodotto cartesiano di tutte le tabelle coinvolte, applicare qualsiasi criterio JOIN, applicare i criteri WHERE, eseguire qualsiasi GROUPing, applicare i criteri HAVING, applicare qualsiasi ORDINAZIONE), ma nel mondo reale, sarebbe molto ingenuo (e male performante) Implementazione SQL che funzionava effettivamente secondo il modello concettuale. Fintanto che i risultati sono equivalenti, le implementazioni SQL sono libere di agire su una query come ritengono opportuno. –

3

È perfettamente legale utilizzare le operazioni di effetti collaterali. Confronto:

"crabapple" 
    .OrderBy(c => { Console.Write(c); return c; }) 
    .Where(c => { Console.Write(c); return c > 'c'; }) 
    .Count(); 
"crabapple" 
    .Where(c => { Console.Write(c); return c > 'c'; }) 
    .OrderBy(c => { Console.Write(c); return c; }) 
    .Count(); 
+0

interessante. Grazie! – gdoron

+0

Solo per aggiungere e impedire alle persone di sparare ai propri piedi. Avere effetti collaterali su un predicato o in particolare su un confronto causerà un comportamento imprevedibile. La quantità di volte che il comparatore è chiamato può anche dipendere da un punto di pivot scelto a caso, e anche per la stessa operazione il metodo può essere chiamato più volte. --- Un predicato in generale è più sicuro, poiché verrà eseguito per tutti gli elementi, per ogni elemento fino a una corrispondenza o un'altra condizione predefinita. Tuttavia potrebbe essere imprevedibile a seconda dell'input o dell'ordine di input. --- Probabilmente è meglio non avere effetti collaterali. – Aidiakapi

Problemi correlati