LINQ Single vs First

LINQ:

¿Es más eficiente usar el operador Single() sobre First() cuando sé con certeza que la consulta devolverá un solo registro ?

¿Hay una diferencia?

Si esperas un solo registro, siempre es bueno ser explícito en tu código.

Sé que otros han escrito por qué usas uno u otro, pero pensé que ilustraría por qué NO deberías usar uno, cuando te refieres al otro.

Nota: En mi código, generalmente FirstOrDefault() y SingleOrDefault() pero esa es una pregunta diferente.

Tomemos, por ejemplo, una tabla que almacena Customers en diferentes idiomas usando una clave compuesta ( ID , Lang ):

 DBContext db = new DBContext(); Customer customer = db.Customers.Where( c=> c.ID == 5 ).First(); 

Este código anterior presenta un posible error de lógica (difícil de rastrear). Devolverá más de un registro (suponiendo que tenga el registro de cliente en varios idiomas) pero siempre devolverá solo el primero … lo que puede funcionar a veces … pero no a otros. Es impredecible

Como su intención es devolver un solo Customer use Single() ;

Lo siguiente arrojaría una excepción (que es lo que quiere en este caso):

 DBContext db = new DBContext(); Customer customer = db.Customers.Where( c=> c.ID == 5 ).Single(); 

Entonces, simplemente da un golpe en la frente y dite a ti mismo … ¡OOPS! ¡Olvidé el campo del idioma! A continuación está la versión correcta:

 DBContext db = new DBContext(); Customer customer = db.Customers.Where( c=> c.ID == 5 && c.Lang == "en" ).Single(); 

First() es útil en el siguiente escenario:

 DBContext db = new DBContext(); NewsItem newsitem = db.NewsItems.OrderByDescending( n => n.AddedDate ).First(); 

Devolverá UN objeto y, como está utilizando la clasificación, será el registro más reciente que se devuelva.

Usar Single() cuando sientas que debe devolver explícitamente siempre 1 registro, te ayudará a evitar errores de lógica.

Single arrojará una excepción si encuentra más de un registro que coincida con los criterios. Primero siempre seleccionará el primer registro de la lista. Si la consulta devuelve solo 1 registro, puede ir con First() .

Ambos lanzarán una excepción InvalidOperationException si la colección está vacía. Alternativamente, puede usar SingleOrDefault() . Esto no lanzará una excepción si la lista está vacía

Hay una sutil diferencia semántica entre estos dos métodos.

Use Single para recuperar el primer (y único) elemento de una secuencia que debe contener un elemento y no más. Si la secuencia tiene más que un elemento, su invocación de Single causará una excepción ya que indicó que solo debería haber un elemento.

Use First para recuperar el primer elemento de una secuencia que puede contener cualquier cantidad de elementos. Si la secuencia tiene más que un elemento, su invocación de First no causará una excepción ya que indicó que solo necesita el primer elemento de la secuencia y no le importa si existen más.

Si la secuencia no contiene elementos, ambas llamadas al método provocarán excepciones, ya que ambos métodos esperan que al menos un elemento esté presente.

Soltero()

Devuelve un único elemento específico de una consulta

Cuándo usar : si se espera exactamente 1 elemento; no 0 o más que 1. Si la lista está vacía o tiene más de un elemento, emitirá una excepción “La secuencia contiene más de un elemento”

SingleOrDefault ()

Devuelve un único elemento específico de una consulta, o un valor predeterminado si no se encuentra ningún resultado

Cuando se usa : cuando se esperan 0 o 1 elementos. Lanzará una excepción si la lista tiene 2 o más elementos.

Primero()

Devuelve el primer elemento de una consulta con resultados múltiples.

Cuándo usar : cuando se esperan 1 o más elementos y solo desea el primero. Lanzará una excepción si la lista no contiene elementos.

FirstOrDefault ()

Devuelve el primer elemento de una lista con cualquier cantidad de elementos, o un valor predeterminado si la lista está vacía.

Cuándo usar : cuando se esperan múltiples elementos y solo desea el primero. O la lista está vacía y desea un valor predeterminado para el tipo especificado, lo mismo que el default(MyObjectType) . Por ejemplo: si el tipo de lista es list , devolverá el primer número de la lista o 0 si la lista está vacía. Si es list , devolverá la primera cadena de la lista o null si la lista está vacía.

Si recuerdo, Single () verifica si hay otro elemento después del primero (y arroja una excepción si es el caso), mientras que First () se detiene después de obtenerlo. Ambos lanzan una excepción si la secuencia está vacía.

Personnally, siempre uso First ().

Si no desea específicamente lanzar una excepción en el caso de que haya más de un elemento, use First() .

Ambos son eficientes, toma el primer artículo. First() es ligeramente más eficiente porque no se molesta en verificar si hay un segundo elemento.

La única diferencia es que Single() espera que haya solo un elemento en la enumeración para comenzar, y lanzará una excepción si hay más de uno. Utiliza .Single() si desea específicamente lanzar una excepción en este caso.

Respecto a la peformance: un compañero de trabajo y yo estábamos discutiendo el rendimiento de Single vs First (o SingleOrDefault versus FirstOrDefault), y estaba argumentando que First (o FirstOrDefault) sería más rápido y mejoraría el rendimiento (estoy a punto de hacer que nuestra aplicación corre más rápido).

He leído varias publicaciones en Stack Overflow que debaten esto. Algunos dicen que hay pequeñas ganancias de rendimiento usando First en vez de Single. Esto se debe a que First simplemente devolverá el primer elemento, mientras que Single debe analizar todos los resultados para asegurarse de que no haya un duplicado (es decir, si encontró el artículo en la primera fila de la tabla, aún escanearía cada dos filas para asegúrese de que no haya un segundo valor que coincida con la condición que arrojaría un error). Sentí que estaba en terreno firme con “Primero” siendo más rápido que “Soltero”, así que decidí probarlo y poner el debate a descansar.

Configuré una prueba en mi base de datos y agregué 1,000,000 de filas de ID UniqueIdentifier Foreign UniqueIdentifier Info nvarchar (50) (relleno con cadenas de números “0” a “999,9999”

Cargué los datos y establecí el ID como campo de clave principal.

Usando LinqPad, mi objective era mostrar que si buscabas un valor en ‘Extranjero’ o ‘Información’ usando Single, sería mucho peor que usar First.

No puedo explicar los resultados que obtuve. En casi todos los casos, usar Single or SingleOrDefault fue un poco más rápido. Esto no tiene ningún sentido lógico para mí, pero quería compartir eso.

Ejemplo: utilicé las siguientes consultas:

 var q = TestTables.First(x=>x.Info == "314638") ; //Vs. Var q = TestTables.Single(x=>x.Info =="314638") ; //(this was slightly faster to my surprise) 

Intenté consultas similares en el campo clave ‘Extranjero’ que no estaba indexado pensando que probaría Primero es más rápido, pero el Single siempre fue un poco más rápido en mis pruebas.

Ellos son diferentes. Ambos afirman que el conjunto de resultados no está vacío, pero solo también afirma que no hay más de 1 resultado. Yo personalmente uso Single en los casos en que solo espero que haya 1 resultado solo porque obtener más de 1 resultado es un error y probablemente deba tratarse como tal.

Puedes probar un ejemplo simple para obtener la diferencia. Se arrojará una excepción en la línea 3;

  List records = new List{1,1,3,4,5,6}; var record = records.First(x => x == 1); record = records.Single(x => x == 1); 

Mucha gente que conozco usa FirstOrDefault (), pero tiendo a usar SingleOrDefault () más porque a menudo sería un tipo de inconsistencia de datos si hubiera más de uno. Esto está lidiando con LINQ-to-Objects, sin embargo.