LINQ: Cuándo usar SingleOrDefault vs. FirstOrDefault () con criterios de filtrado


Considere los métodos de extensiónumumerable SingleOrDefault() y FirstOrDefault()

MSDN documenta que SingleOrDefault:

Devuelve el único elemento de una secuencia, o un valor predeterminado si la secuencia está vacía; este método lanza una excepción si hay más de un elemento en la secuencia.

Considerando FirstOrDefault de MSDN (presumiblemente cuando se usa un OrderBy() o OrderByDescending() o ninguno en absoluto),

Devuelve el primer elemento de una secuencia

Considere un puñado de consultas de ejemplo, no siempre está claro cuándo usar estos dos métodos:

var someCust = db.Customers
.SingleOrDefault(c=>c.ID == 5); //unlikely(?) to be more than one, but technically COULD BE

var bobbyCust = db.Customers
.FirstOrDefault(c=>c.FirstName == "Bobby"); //clearly could be one or many, so use First?

var latestCust = db.Customers
.OrderByDescending(x=> x.CreatedOn)
.FirstOrDefault();//Single or First, or does it matter?

Pregunta

¿Qué convenciones sigues o sugieres cuando decides usar SingleOrDefault() y FirstOrDefault() en tus consultas LINQ?

Author: p.campbell, 2009-11-17

13 answers

Cada vez que se utiliza SingleOrDefault, se indica claramente que la consulta debe dar como máximo un único resultado. Por otro lado, cuando se usa FirstOrDefault, la consulta puede devolver cualquier cantidad de resultados, pero usted indica que solo desea el primero.

Personalmente encuentro la semántica muy diferente y usar la apropiada, dependiendo de los resultados esperados, mejora la legibilidad.

 376
Author: Bryan Menard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-17 00:04:24

Si su conjunto de resultados devuelve 0 registros:

  • SingleOrDefault devuelve el valor predeterminado para el tipo (por ejemplo, el valor predeterminado para int es 0)
  • FirstOrDefault devuelve el valor predeterminado para el tipo

Si el conjunto de resultados devuelve 1 registro:

  • SingleOrDefault devuelve ese registro
  • FirstOrDefault devuelve ese registro

Si su conjunto de resultados devuelve muchos registros:

  • SingleOrDefault lanza una excepción
  • FirstOrDefault devuelve la primera record

Conclusión:

Si desea que se lance una excepción si el conjunto de resultados contiene muchos registros, use SingleOrDefault.

Si siempre desea 1 registro sin importar lo que contenga el conjunto de resultados, use FirstOrDefault

 491
Author: Alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-04 04:03:24

Hay

  • una diferencia semántica
  • una diferencia de rendimiento

Entre los dos.

Diferencia semántica:

  • FirstOrDefault devuelve un primer elemento potencialmente múltiple (o predeterminado si no existe ninguno).
  • SingleOrDefault asume que hay un solo elemento y lo devuelve (o por defecto si no existe ninguno). Varios artículos son una violación del contrato, se lanza una excepción.

Rendimiento Diferencia

  • FirstOrDefault suele ser más rápido, itera hasta que encuentra el elemento y solo tiene que iterar todo el enumerable cuando no lo encuentra. En muchos casos, hay una alta probabilidad de encontrar un artículo.

  • SingleOrDefault necesita comprobar si solo hay un elemento y por lo tanto siempre itera todo el enumerable. Para ser precisos, itera hasta que encuentra un segundo elemento y lanza una excepción. Pero en la mayoría de los casos, no hay segundo elemento.

Conclusión

  • Use FirstOrDefault si no le importa cuántos elementos hay o cuando no puede permitirse verificar la singularidad (por ejemplo, en una colección muy grande). Cuando se comprueba la singularidad en la adición de los elementos a la colección, puede ser demasiado caro para comprobar de nuevo al buscar esos elementos.

  • Use SingleOrDefault si no tiene que preocuparse demasiado por el rendimiento y desea asegurarse de que la suposición de un solo elemento es claro para el lector y se comprueba en tiempo de ejecución.

En la práctica, se utiliza First / FirstOrDefault a menudo, incluso en los casos en que se asume un solo elemento, para mejorar el rendimiento. Aún deberías recordar eso.Single / SingleOrDefault puede mejorar la legibilidad (porque establece la suposición de un solo elemento) y la estabilidad (porque lo comprueba) y usarlo apropiadamente.

 214
Author: Stefan Steinegger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-19 09:33:42

Nadie ha mencionado que FirstOrDefault traducido en SQL hace TOP 1 registro, y SingleOrDefault hace TOP 2, porque necesita saber si hay más de 1 registro.

 61
Author: shalke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-06 12:55:00

Para LINQ - > SQL:

SingleOrDefault

  • generará una consulta como "select * from users where userid = 1"
  • Seleccione registro coincidente, Lanza excepción si se encuentran más de un registro
  • Úselo si está obteniendo datos basados en la columna de clave primaria/única

FirstOrDefault

  • generará una consulta como "select top 1 * from users where userid = 1"
  • Seleccione las primeras filas coincidentes
  • Utilizar si está obteniendo datos basados en una columna de clave no primaria/única
 9
Author: Prakash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-04 13:35:16

Utilizo SingleOrDefault en situaciones donde mi lógica dicta que el resultado será cero o uno. Si hay más, es una situación de error, lo cual es útil.

 7
Author: spender,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-17 00:05:25

SingleOrDefault: Estás diciendo que "A lo sumo" hay un elemento que coincide con la consulta o predeterminado FirstOrDefault: Estás diciendo que hay "Al menos" un elemento que coincide con la consulta o por defecto

Di eso en voz alta la próxima vez que necesites elegir y probablemente elegirás sabiamente. :)

 5
Author: Steven,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-09-01 16:22:34

En sus casos, usaría lo siguiente:

Select by ID==5: está bien usar SingleOrDefault aquí, porque esperas una entidad [o ninguna], si tienes más de una entidad con ID 5, hay algo mal y definitivamente digno de excepción.

Al buscar personas cuyo nombre de pila es igual a" Bobby", puede haber más de uno (muy posiblemente pensaría), por lo que no debe usar Single ni First, solo seleccione con la operación Where (si "Bobby" devuelve demasiados entidades, el usuario tiene que refinar su búsqueda o elegir uno de los resultados devueltos)

El orden por fecha de creación también debe realizarse con una operación Where (es poco probable que tenga solo una entidad, ordenar no sería de mucha utilidad ;) esto sin embargo implica que desea que TODAS las entidades estén ordenadas-si desea solo UNA, use FirstOrDefault, Single lanzaría cada vez si tiene más de una entidad.

 4
Author: Olli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-17 00:12:07

En tu último ejemplo:

var latestCust = db.Customers
.OrderByDescending(x=> x.CreatedOn)
.FirstOrDefault();//Single or First, or doesn't matter?

, Sí lo hace. Si intenta utilizar SingleOrDefault() y los resultados de la consulta en más de registro que obtendría y excepción. La única vez que puede usar SingleOrDefault() de forma segura es cuando espera solo 1 y solo 1 resultado...

 3
Author: bytebender,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-17 00:07:35

Ambos son los operadores de elemento y se utilizan para seleccionar un solo elemento de una secuencia. Pero hay una pequeña diferencia entre ellos. El operador SingleOrDefault() lanzaría una excepción si más de un elemento cumple la condición donde as FirstOrDefault () no lanzaría ninguna excepción para el mismo. Aquí está el ejemplo.

List<int> items = new List<int>() {9,10,9};
//Returns the first element of a sequence after satisfied the condition more than one elements
int result1 = items.Where(item => item == 9).FirstOrDefault();
//Throw the exception after satisfied the condition more than one elements
int result3 = items.Where(item => item == 9).SingleOrDefault();
 3
Author: Sheo Dayal Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-20 08:34:52

Así que como entiendo ahora, SingleOrDefault será bueno si está consultando datos que se garantiza que sean únicos, es decir, impuestos por restricciones de base de datos como la clave primaria.

O hay una mejor manera de consultar la clave primaria.

Asumiendo que mi TableAcc tiene

AccountNumber - Primary Key, integer
AccountName
AccountOpenedDate
AccountIsActive
etc.

Y quiero consultar un AccountNumber 987654, utilizo

var data = datacontext.TableAcc.FirstOrDefault(obj => obj.AccountNumber == 987654);
 1
Author: M G,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-05 14:05:42

Una cosa que se pierde en las respuestas....

Si hay varios resultados, FirstOrDefault sin un orden por puede traer de vuelta diferentes resultados basados en la estrategia de índice que alguna vez fue utilizada por el servidor.

Personalmente no puedo soportar ver FirstOrDefault en el código porque a mí me dice que al desarrollador no le importaron los resultados. Con una orden por aunque puede ser útil como una forma de hacer cumplir el último/más temprano. He tenido que corregir muchos problemas causados por desarrolladores descuidados usando FirstOrDefault.

 0
Author: El Dude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-11 22:39:17

No entiendo por qué estás usando FirstOrDefault(x=> x.ID == key) cuando esto podría recuperar resultados mucho más rápido si usas Find(key). Si está consultando con la clave principal de la tabla, la regla general es usar siempre Find(key). FirstOrDefault debe usarse para cosas predicadas como (x=> x.Username == username), etc.

Esto no se merece un downvote como el encabezado de la pregunta no era específica de linq en DB o Linq to List/IEnumerable etc.

 -7
Author: Theron Govender,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-29 09:32:40