LINQ:
¿Es más eficiente usar el Single()
operador First()
cuando sé con certeza que la consulta devolverá un solo registro ?
¿Hay una diferencia?
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, normalmente usaré FirstOrDefault()
y SingleOrDefault()
esa es una pregunta diferente.
Tome, por ejemplo, una tabla que se 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 introduce un posible error lógico (difícil de rastrear). Devolverá más de un registro (suponiendo que tenga el registro del cliente en varios idiomas) pero siempre devolverá solo el primero ... que puede funcionar a veces ... pero no en otros. Es impredecible
Dado que su intención es devolver un Customer
uso único 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();
Luego, simplemente te golpeas en la frente y te dices a ti mismo ... ¡Vaya! ¡Olvidé el campo del idioma! La siguiente es 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 dado que está utilizando la clasificación, será el registro más reciente que se devuelva.
Usarlo Single()
cuando sienta que debe devolver explícitamente siempre 1 registro lo ayudará a evitar errores lógicos.
customers.Where(predicate).Single()
customers.Single(predicate)
?Single lanzará 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
InvalidOperationException
excepción si la colección está vacía. Alternativamente puedes usarSingleOrDefault()
. Esto no arrojará una excepción si la lista está vacíafuente
Soltero()
SingleOrDefault ()
Primero()
FirstOrDefault ()
fuente
First
cuando se esperan 1 o más elementos , no solo "más de 1" yFirstOrDefault
con cualquier cantidad de elementos.Hay una sutil diferencia semántica entre estos dos métodos.
Utilícelo
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 de un elemento, su invocación deSingle
provocará una excepción ya que usted indicó que solo debería haber un elemento.Utilícelo
First
para recuperar el primer elemento de una secuencia que puede contener cualquier número de elementos. Si la secuencia tiene más de un elemento, su invocación deFirst
, no provocará 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 a métodos provocarán excepciones, ya que ambos métodos esperan que al menos un elemento esté presente.
fuente
Si no desea que se arroje una excepción específicamente en caso de que haya más de un elemento, úselo
First()
.Ambos son eficientes, toma el primer artículo.
First()
es un poco más eficiente porque no se molesta en verificar si hay un segundo elemento.La única diferencia es que
Single()
espera que solo haya un elemento en la enumeración, y arrojará una excepción si hay más de uno. Se usa.Single()
si desea específicamente que se arroje una excepción en este caso.fuente
Si recuerdo, Single () comprueba si hay otro elemento después del primero (y lanza 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.
Personalmente, siempre uso Primero ().
fuente
Con respecto al rendimiento: un compañero de trabajo y yo estábamos discutiendo el rendimiento de Single vs First (o SingleOrDefault vs FirstOrDefault), y estaba argumentando que First (o FirstOrDefault) sería más rápido y mejoraría el rendimiento (estoy a punto de crear 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 con First en lugar de Single. Esto se debe a que Primero simplemente devolverá el primer elemento, mientras que Single debe escanear todos los resultados para asegurarse de que no haya un duplicado (es decir: si encuentra el elemento en la primera fila de la tabla, aún escaneará cada dos filas para asegúrese de que no haya un segundo valor que coincida con la condición que luego arrojaría un error). Sentí que estaba en tierra firme con "Primero" siendo más rápido que "Soltero", así que me propuse probarlo y dejar el debate.
Configuré una prueba en mi base de datos y agregué 1,000,000 de filas de ID UniqueIdentifier Foreign UniqueIdentifier Info nvarchar (50) (lleno de cadenas de números "0" a "999,9999"
Cargué los datos y configuré la ID como un campo de clave principal.
Con LinqPad, mi objetivo era mostrar que si buscaba 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 o SingleOrDefault fue un poco más rápido. Esto no tiene ningún sentido lógico para mí, pero quería compartirlo.
Ej: utilicé las siguientes consultas:
Intenté consultas similares en el campo clave 'Extranjero' que no estaba indexado pensando que demostraría que Primero es más rápido, pero Single siempre fue un poco más rápido en mis pruebas.
fuente
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. 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 debería tratarse como tal.
fuente
Puedes probar un ejemplo simple para obtener la diferencia. Se lanzará una excepción en la línea 3;
fuente
Mucha gente que conozco usa FirstOrDefault (), pero tiendo a usar SingleOrDefault () más porque a menudo sería algún tipo de inconsistencia de datos si hubiera más de uno. Sin embargo, se trata de LINQ-to-Objects.
fuente
Los registros en la entidad Empleado:
Employeeid = 1
: Solo un empleado con esta identificaciónFirstname = Robert
: Más de un empleado con este nombreEmployeeid = 10
: Ningún empleado con esta identificaciónAhora es necesario entender qué
Single()
yFirst()
significar en detalle.Soltero()
Single () se utiliza para devolver un único registro que existe de forma única en una tabla, por lo que la consulta a continuación devolverá el Empleado cuyo
employeed =1
porque tenemos solo un Empleado cuyoEmployeed
es 1. Si tenemos dos registros paraEmployeeId = 1
entonces arroja un error (vea el error a continuación en la segunda consulta donde estamos usando un ejemplo paraFirstname
.Lo anterior devolverá un solo registro, que tiene 1
employeeId
Lo anterior arrojará una excepción porque los registros múltiples están en la tabla para
FirstName='Robert'
. La excepción seráEsto, nuevamente, arrojará una excepción porque no existe un registro para id = 10. La excepción será
Porque
EmployeeId = 10
devolverá nulo, pero a medida queSingle()
lo usemos arrojará un error. Para manejar el error nulo debemos usarSingleOrDefault()
.Primero()
Primero () devuelve de múltiples registros los registros correspondientes ordenados en orden ascendente de acuerdo con
birthdate
lo que devolverá 'Robert', que es el más antiguo.Arriba debería devolver el más antiguo, Robert según DOB.
Lo anterior arrojará una excepción ya que no existe un registro para id = 10. Para evitar una excepción nula, deberíamos usar en
FirstOrDefault()
lugar deFirst()
.Nota: Solo podemos usar
First()
/Single()
cuando estemos absolutamente seguros de que no puede devolver un valor nulo.En ambas funciones, use SingleOrDefault () OR FirstOrDefault () que manejará una excepción nula, en el caso de que no se encuentre ningún registro, devolverá nulo.
fuente