2010-12-08 15 views
7

Dadas las siguientes declaraciones LINQ, ¿cuál será más eficiente?LINQ ToList(). Tome (10) vs Take (10) .ToList() cuál genera una consulta más eficiente

UNO:

public List<Log> GetLatestLogEntries() 
{ 
    var logEntries = from entry in db.Logs 
       select entry; 
    return logEntries.ToList().Take(10); 
} 

DOS:

public List<Log> GetLatestLogEntries() 
{ 
    var logEntries = from entry in db.Logs 
       select entry; 
    return logEntries.Take(10).ToList(); 
} 

Soy consciente de que .ToList() realiza la consulta inmediatamente.

Respuesta

17

La primera versión no compilaría, porque el valor de retorno de Take es IEnumerable<T>, no List<T>. Por lo que se necesitaría para ser:

public List<Log> GetLatestLogEntries() 
{ 
    var logEntries = from entry in db.Logs 
       select entry; 
    return logEntries.ToList().Take(10).ToList(); 
} 

que se vendería todos los datos de la base de datos y convertirla en una lista, a continuación, toman las primeras 10 entradas, a continuación, convertirla en una lista de nuevo .

Conseguir el Take(10) que se produzca en la base de datos (es decir, la segunda forma) sin duda se ve un diablos de mucho más barato para mí ...

Tenga en cuenta que no hay método Queryable.ToList() - que va a terminar llamando a la que Enumerable.ToList() obtendrá todas las entradas. En otras palabras, la llamada a ToListno incluye en la traducción de SQL, mientras que Take lo hace.

También tenga en cuenta que el uso de una expresión de consulta aquí tampoco tiene mucho sentido. Me gustaría escribir como:

public List<Log> GetLatestLogEntries() 
{ 
    return db.Log.Take(10).ToList(); 
} 

Eso sí, es posible que desee un OrderBy llamada - de lo contrario, sólo tomará las primeras 10 entradas que encuentra, que tal vez no sean los más recientes ...

+0

Gracias, Jon, por esto. Muy útil. –

2

La segunda versión será más eficiente (en uso de tiempo y memoria). Por ejemplo, imagine que tiene una secuencia que contiene 1.000.000 artículos:

  1. La versión primeras itera a través de todos los elementos 1.000.000, añadiéndolos a una lista que va. Luego, finalmente, tomará los primeros 10 ítems de esa gran lista.

  2. La segunda versión solo necesita repetir los primeros 10 elementos y agregarlos a una lista. (Los restantes 999.990 artículos ni siquiera necesitan ser considerados.)

2

Su primera opción no funcionará, porque .Take(10) convierte en IEnumerable<Log>. Su tipo de devolución es List<Log>, por lo que tendría que hacer return logEntries.ToList().Take(10).ToList(), que es más ineficiente.

Al hacer .ToList().Take(10), está forzando que .Take(10) sea LINQ a los objetos, mientras que, de otra manera, el filtro podría pasarse a la base de datos u otra fuente de datos subyacente. En otras palabras, si primero hace .ToList(), TODOS los objetos tienen que ser transferidos de la base de datos y asignados en la memoria. ENTONCES filtra a los primeros 10. Si está hablando de millones de filas de bases de datos (y objetos), puede imaginarse que esto es MUY ineficiente y no escalable.

El segundo también se ejecutará inmediatamente porque tiene .ToList(), por lo que no hay diferencia.

0

La segunda opción.

El primero evaluará todo el enumerable, sorbiéndolo en una Lista(); luego configura el iterador que recorrerá los primeros diez objetos y luego saldrá.

El segundo configura el iterador Take() primero, así que pase lo que pase después, solo 10 objetos serán evaluados y enviados al procesamiento "downstream" (en este caso ToList() que tomará esos diez elementos y devolverlos como la Lista concreta).

1

¿Qué tal esto? Versión

I have 5000 records in "items" 

1:

IQueryable<T> items = Items; // my items 
    items = ApplyFilteringCriteria(items, filter); // my filter BL 
    items = ApplySortingCriteria(items, sortBy, sortDir); // my sorting BL 
    items = items.Skip(0); 
    items = items.Take(25); 
    return items.ToList(); 

esto tuvo: 20 seg en el servidor versión

2:

IQueryable<T> items = Items; // my items 
    items = ApplyFilteringCriteria(items, filter); // my filter BL 
    items = ApplySortingCriteria(items, sortBy, sortDir); // my sorting BL 
    List<T> x = items.ToList(); 
    items = x.Skip(0).ToList(); 
    items = x.Take(25).ToList(); 
    return x; 

esto tuvo: 1 seg en el servidor

Lo ¿piensas ahora? ¿Alguna idea de por qué?

+0

La versión 1 es Iqueryable, lo que significa que consultará su base de datos. Linq no siempre es tan eficiente con eso. La versión 2 la coloca en una lista, lo que significa que a partir de ese momento no se activarán las consultas en la base de datos. –

Cuestiones relacionadas