.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