Respuestas:
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 conFirst()
.
Ambos lanzarán una InvalidOperationException
excepción si la colección está vacía. Alternativamente puedes usar SingleOrDefault()
. Esto no arrojará una excepción si la lista está vacía
Soltero()
Devuelve un único elemento específico de una consulta.
Cuándo usar : si se espera exactamente 1 elemento; no 0 o más de 1. Si la lista está vacía o tiene más de un elemento, arrojará 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 múltiples resultados.
Cuando se usa : cuando se esperan 1 o más elementos y solo quieres 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 varios elementos y solo quieres el primero. O la lista está vacía y desea un valor predeterminado para el tipo especificado, igual que
default(MyObjectType)
. Por ejemplo: si el tipo de lista eslist<int>
, devolverá el primer número de la lista o 0 si la lista está vacía. Si eslist<string>
así, devolverá la primera cadena de la lista o nula si la lista está vacía.
First
cuando se esperan 1 o más elementos , no solo "más de 1" y FirstOrDefault
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.
Si no desea que se arroje una excepción específicamente en caso de que haya más de un elemento, úseloFirst()
.
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.
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 ().
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:
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 demostraría que Primero es más rápido, pero 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. 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.
Puedes probar un ejemplo simple para obtener la diferencia. Se lanzará una excepción en la línea 3;
List<int> records = new List<int>{1,1,3,4,5,6};
var record = records.First(x => x == 1);
record = records.Single(x => x == 1);
Los registros en la entidad Empleado:
Employeeid = 1
: Solo un empleado con esta identificación
Firstname = Robert
: Más de un empleado con este nombre
Employeeid = 10
: Ningún empleado con esta identificación
Ahora es necesario entender qué Single()
y First()
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 cuyo Employeed
es 1. Si tenemos dos registros para EmployeeId = 1
entonces arroja un error (vea el error a continuación en la segunda consulta donde estamos usando un ejemplo para Firstname
.
Employee.Single(e => e.Employeeid == 1)
Lo anterior devolverá un solo registro, que tiene 1 employeeId
Employee.Single(e => e.Firstname == "Robert")
Lo anterior arrojará una excepción porque los registros múltiples están en la tabla para FirstName='Robert'
. La excepción será
InvalidOperationException: la secuencia contiene más de un elemento
Employee.Single(e => e.Employeeid == 10)
Esto, nuevamente, arrojará una excepción porque no existe un registro para id = 10. La excepción será
InvalidOperationException: la secuencia no contiene elementos.
Porque EmployeeId = 10
devolverá nulo, pero a medida que Single()
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.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Firstname == "Robert")
Arriba debería devolver el más antiguo, Robert según DOB.
Employee.OrderBy(e => e. Birthdate)
.First(e => e.Employeeid == 10)
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.