singleordefault firstordefault .net linq linq-to-sql

.net - LINQ: cuándo usar SingleOrDefault contra FirstOrDefault() con criterios de filtrado



singleordefault c# (13)

Ahi esta

  • una diferencia semantica
  • una diferencia de rendimiento

entre los dos.

Diferencia semántica:

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

Diferencia de rendimiento

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

  • SingleOrDefault necesita verificar si hay solo 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 un segundo elemento.

Conclusión

  • Use FirstOrDefault si no le importa cuántos elementos hay o cuando no puede pagar la singularidad (por ejemplo, en una colección muy grande). Cuando verifique la singularidad al agregar los elementos a la colección, puede ser demasiado costoso volver a verificarlos cuando busque esos elementos.

  • Utilice SingleOrDefault si no tiene que preocuparse demasiado por el rendimiento y desea asegurarse de que la suposición de un solo elemento sea clara para el lector y verificada en tiempo de ejecución.

En la práctica, usa First / FirstOrDefault menudo, incluso en los casos en que asume un solo elemento, para mejorar el rendimiento. Aún debe recordar que Single / SingleOrDefault puede mejorar la legibilidad (porque establece el supuesto de un solo elemento) y la estabilidad (porque lo verifica) y usarlo de manera adecuada.

Considere los métodos de extensión 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.

mientras que FirstOrDefault de MSDN (probablemente cuando se utiliza 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 sigue o sugiere cuando decide utilizar SingleOrDefault() y FirstOrDefault() en sus consultas LINQ?


Ambos son los operadores de elementos y se utilizan para seleccionar un solo elemento de una secuencia. Pero hay una diferencia menor entre ellos. El operador SingleOrDefault () lanzaría una excepción si más de un elemento satisface la condición en que, como FirstOrDefault (), no lanzará ninguna excepción por 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();


En sus casos, usaría lo siguiente:

seleccione por ID == 5: está bien usar SingleOrDefault aquí, porque espera una entidad [o ninguna], si tiene más de una entidad con ID 5, hay algo incorrecto y definitivamente una excepción.

al buscar personas cuyo primer nombre sea igual a "Bobby", puede haber más de uno (posiblemente, creo), por lo que no debe usar Único ni Primero, simplemente 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 (no es probable que tenga una sola entidad, la clasificación no sería de mucha utilidad); sin embargo, esto implica que desea que TODAS las entidades estén ordenadas; si solo quiere UNA, use FirstOrDefault, Solo tirarías cada vez si tuvieras más de una entidad.


En tu último ejemplo:

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

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


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


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

esto no merecía un voto negativo ya que el encabezado de la pregunta no era específico de linq en DB o Linq to List / IEnumerable, etc.


Por lo que entiendo ahora, SingleOrDefault será bueno si está buscando datos que se garantiza que sean únicos, es decir, aplicados por restricciones de DB como clave principal.

O hay una mejor manera de consultar la clave principal.

Suponiendo que mi TableAcc tiene

AccountNumber - Primary Key, integer AccountName AccountOpenedDate AccountIsActive etc.

y quiero consultar un número de cuenta AccountNumber 987654 , yo uso

var data = datacontext.TableAcc.FirstOrDefault(obj => obj.AccountNumber == 987654);


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 el primer registro

Conclusión:

Si desea que se SingleOrDefault 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


Siempre que use SingleOrDefault , claramente SingleOrDefault que la consulta debe dar como resultado un resultado único . Por otro lado, cuando se usa FirstOrDefault , la consulta puede devolver cualquier cantidad de resultados, pero usted FirstOrDefault que solo desea el primero.

Personalmente encuentro que la semántica es muy diferente y el uso de la apropiada, dependiendo de los resultados esperados, mejora la legibilidad.


SingleOrDefault: estás diciendo que "como máximo" 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 predeterminado

Dígalo en voz alta la próxima vez que necesite elegir y probablemente elija sabiamente. :)


Una cosa que se pierde en las respuestas ...

Si hay varios resultados, FirstOrDefault sin un pedido puede devolver resultados diferentes según la estrategia de índice utilizada por el servidor.

Personalmente, no puedo soportar ver FirstOrDefault en el código porque para mí dice que al desarrollador no le importaron los resultados. Con un pedido por, aunque puede ser útil como una forma de hacer cumplir lo último / más antiguo. He tenido que corregir muchos problemas causados ​​por desarrolladores descuidados que usan FirstOrDefault.


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


Para LINQ -> SQL:

SingleOrDefault

  • generará una consulta como "seleccionar * de usuarios donde userid = 1"
  • Seleccione el registro coincidente, lanza la excepción si se encuentran más de un registro
  • Úselo si está obteniendo datos en función de la columna de clave principal / única

FirstOrDefault

  • generará una consulta como "seleccione top 1 * de los usuarios donde userid = 1"
  • Seleccionar primeras filas coincidentes
  • Úselo si está obteniendo datos basados ​​en una columna de clave no primaria / única