2012-05-04 21 views
29

Estamos poniendo en práctica algunos repositorios de datos EF, y tenemos algunas consultas que incluirían TOP 1
Con Entity Framework, ¿es mejor usar .First() o .Take (1) para "TOP 1"?

He leído muchos mensajes que sugieren utilizar .Take(1)
El código que estoy revisando usos .First()

entiendo que ambos producen el mismo resultado para la asignación de objetos, pero ¿ambos resuelven la misma consulta? Cuando se consulta el DB, ¿será realmente con TOP 1 para ambas solicitudes? ¿O ejecutarán la consulta en su totalidad en el enumerable, y luego simplemente tomarán la primera entrada en la colección?

Además, si usamos .FirstOrDefault() entonces ¿por qué deberíamos esperar un comportamiento diferente? Sé que cuando uso un IEnumerable, lanzaré .First() en una colección vacía, pero si este es en realidad solo cambiando la consulta para incluir TOP 1 entonces no debería esperar ninguna diferencia funcional entre .First() y .FirstOrDefault() .... ¿verdad?

Alternativamente, ¿hay algún método mejor que estas extensiones enumerables para hacer que la consulta ejecute TOP 1?

+0

¿Cómo se convierte la consulta '.Take (1)' en un objeto? – Gabe

+0

Use 'First()' si quiere un artículo. Use 'Take (1)' si desea una secuencia de un solo elemento. –

Respuesta

53

De LINQPad:

C#:

age_Centers.Select(c => c.Id).First(); 
age_Centers.Select(c => c.Id).FirstOrDefault(); 
age_Centers.Select(c => c.Id).Take(1).Dump(); 

SQL:

SELECT TOP (1) [t0].[Id] 
FROM [age_Centers] AS [t0] 
GO 

SELECT TOP (1) [t0].[Id] 
FROM [age_Centers] AS [t0] 
GO 

SELECT TOP (1) [t0].[Id] 
FROM [age_Centers] AS [t0] 

* Tenga en cuenta que Take(1) enumera y devuelve un IQueryable.

+3

+1 para demostrar que crean el mismo resultado de consulta. – Matthew

+3

¿Qué ocurre con query.Skip (10) .Take (1)? ¿Son esas tres consultas lo mismo? –

5

Redirigir la propiedad DataContext Log a Console.Out o a un archivo de texto y ver qué consulta genera cada opción.

+3

Esto probablemente fue pensado para ser un comentario, ¿verdad? – dasblinkenlight

+3

@dasblinkenlight No, no lo era. Creo que es una respuesta válida. Le digo al OP cómo encontrar la respuesta por sí mismo; fácilmente. – Icarus

+0

¿Cómo puedo hacer esto cuando mi contexto es el edmx? No expone una propiedad .Log. – Matthew

1

Cómo .First() obras:

Si la colección es de tipo IList, entonces el primer elemento se accede por la posición de índice que es diferente dependiendo de la implementación de la colección. De lo contrario, un iterador devuelve el primer elemento.

Y .Take(int count) siempre iterar.

Si hay alguna ganancia, sucede si la colección implementa IList y la velocidad para acceder al primer elemento por índice es mayor que la de devolver un iterador. No creo que sea significativo. ;)

Fuentes:

http://www.hookedonlinq.com/FirstOperator.ashx

http://www.hookedonlinq.com/TakeOperator.ashx

+1

Sé que es así como '.First()' funciona en un enumerable. Estoy preguntando en el contexto de la consulta producida aquí. – Matthew

1

Primero consultará Tomar 1, por lo que no hay diferencia en la consulta.Llamar a FirstOrDefault será una declaración de un paso, porque Take returns IEnumerable necesitará llamar a First de todos modos.

Primero lanzará una excepción, por lo que FirstOrDefault siempre es preferible.

Y, por supuesto, las personas que escribieron el convertidor de consultas EF son lo suficientemente inteligentes como para llamar a Take 1 en lugar de ejecutar el conjunto de resultados completo y devolver el primer elemento.

Puede verificar esto usando el generador de perfiles SQL.

2
**First()** operates on a collection of any number of objects and returns the first object.  **Take(1)** operates on a collection of any number of objects and returns a collection containing the first object. 

También puede utilizar solo Individual() opera en una colección de exactamente un objeto y simplemente devuelve el objeto.

+0

Relacionado: [LINQ Single vs First] (https://stackoverflow.com/questions/2724096/linq-single-vs-first) – Sinjai

Cuestiones relacionadas