LINQ: Cuándo utilizar SingleOrDefault frente a FirstOrDefault() con criterios de filtrado
Considere los métodos de extensión IEnumerable SingleOrDefault()
yFirstOrDefault()
MSDN documenta queSingleOrDefault
:
Devuelve el único elemento de una secuencia, o un valor predeterminado si la secuencia está vacía; este método genera una excepción si hay más de un elemento en la secuencia.
mientras que FirstOrDefault
desde MSDN (presumiblemente cuando se usa OrderBy()
o OrderByDescending()
ninguno),
Devuelve el primer elemento de una secuencia.
Considere un puñado de consultas de ejemplo; no siempre está claro cuándo utilizar 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 al decidir utilizar SingleOrDefault()
y FirstOrDefault()
en sus consultas LINQ?
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 su conjunto de resultados devuelve 1 registro:
SingleOrDefault
devuelve ese registroFirstOrDefault
devuelve ese registro
Si su conjunto de resultados devuelve muchos registros:
SingleOrDefault
lanza una excepciónFirstOrDefault
devuelve el primer registro
Conclusión:
Si desea que se produzca una excepción si el conjunto de resultados contiene muchos registros, utilice SingleOrDefault
.
Si siempre desea 1 registro sin importar lo que contenga el conjunto de resultados, useFirstOrDefault
Siempre que utilice SingleOrDefault
, indique claramente que la consulta debe generar como máximo un resultado único . Por otro lado, cuando FirstOrDefault
se usa, la consulta puede devolver cualquier cantidad de resultados, pero usted indica que solo desea el primero.
Personalmente encuentro que la semántica es muy diferente y usar la adecuada, dependiendo de los resultados esperados, mejora la legibilidad.
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 artículo y lo devuelve (o lo devuelve por defecto si no existe ninguno). Varios elementos constituyen una violación del 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 el enumerable cuando no lo encuentra. En muchos casos, existe una alta probabilidad de encontrar un artículo.SingleOrDefault
necesita verificar si solo hay un elemento y, por lo tanto, siempre itera todo el enumerable. En teoría, podría dejar de iterar cuando encuentre un segundo elemento y generar una excepción. Pero en la mayoría de los casos no existe un segundo elemento, por lo que no ayuda mucho.
Conclusión
Úselo
FirstOrDefault
si no le importa cuántos elementos hay o cuando no puede permitirse el lujo de verificar la unicidad (por ejemplo, en una colección muy grande). Cuando verifica la singularidad al agregar elementos a la colección, puede resultar demasiado costoso verificarlo nuevamente al buscar esos elementos.Úselo
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 se verifique en tiempo de ejecución.
En la práctica, se utiliza First
/ FirstOrDefault
a menudo incluso en los casos en los que se asume un solo elemento, para mejorar el rendimiento. Aún debes recordar que Single
/ SingleOrDefault
puede mejorar la legibilidad (porque establece la suposición de un solo elemento) y la estabilidad (porque lo verifica) y usarlo apropiadamente.