LINQ:
Est-il plus efficace d'utiliser l' Single()
opérateur First()
lorsque je sais avec certitude que la requête renverra un seul enregistrement ?
Y a-t-il une différence?
Je sais que d'autres ont écrit pourquoi vous utilisez l'un ou l'autre, mais j'ai pensé illustrer pourquoi vous ne devriez PAS utiliser l'un, quand vous parlez de l'autre.
Remarque: Dans mon code, je vais généralement utiliser FirstOrDefault()
et SingleOrDefault()
mais c'est une question différente.
Prenons, par exemple, une table qui stocke Customers
dans différentes langues à l'aide d'une clé composite ( ID
, Lang
):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).First();
Ce code ci-dessus introduit une erreur logique possible (difficile à tracer). Il renverra plus d'un enregistrement (en supposant que vous avez l'enregistrement client en plusieurs langues) mais il ne renverra toujours que le premier ... qui peut fonctionner parfois ... mais pas d'autres. C'est imprévisible.
Puisque votre intention est de retourner un Customer
usage unique Single()
;
Ce qui suit lèverait une exception (ce que vous voulez dans ce cas):
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 ).Single();
Ensuite, vous vous frappez simplement sur le front et vous dites ... OOPS! J'ai oublié le champ de langue! Voici la bonne version:
DBContext db = new DBContext();
Customer customer = db.Customers.Where( c=> c.ID == 5 && c.Lang == "en" ).Single();
First()
est utile dans le scénario suivant:
DBContext db = new DBContext();
NewsItem newsitem = db.NewsItems.OrderByDescending( n => n.AddedDate ).First();
Il renverra UN objet, et puisque vous utilisez le tri, ce sera l'enregistrement le plus récent qui sera renvoyé.
L'utilisation Single()
lorsque vous pensez qu'il doit toujours renvoyer explicitement 1 enregistrement vous aidera à éviter les erreurs de logique.
customers.Where(predicate).Single()
customers.Single(predicate)
?Single lèvera une exception s'il trouve plus d'un enregistrement correspondant aux critères. First sélectionnera toujours le premier enregistrement de la liste. Si la requête ne renvoie qu'un seul enregistrement, vous pouvez utiliser
First()
.Les deux lèveront une
InvalidOperationException
exception si la collection est vide. Vous pouvez également utiliserSingleOrDefault()
. Cela ne lèvera pas d'exception si la liste est videla source
Célibataire()
SingleOrDefault ()
Première()
FirstOrDefault ()
la source
First
lorsque 1 ou plusieurs éléments sont attendus , pas seulement "plus de 1", etFirstOrDefault
avec n'importe quelle quantité d'éléments.Il existe une différence sémantique subtile entre ces deux méthodes.
Permet
Single
de récupérer le premier (et le seul) élément d'une séquence qui doit contenir un élément et pas plus. Si la séquence contient plus d'un élément, votre invocation deSingle
provoquera une exception, car vous avez indiqué qu'il ne devrait y avoir qu'un seul élément.Permet
First
de récupérer le premier élément d'une séquence pouvant contenir n'importe quel nombre d'éléments. Si la séquence contient plus d'un élément, votre invocation deFirst
ne provoquera pas la levée d'une exception puisque vous avez indiqué que vous n'avez besoin que du premier élément de la séquence et que vous vous en fichez s'il en existe d'autres.Si la séquence ne contient aucun élément, les deux appels de méthode provoquent la levée d'exceptions car les deux méthodes s'attendent à ce qu'au moins un élément soit présent.
la source
Si vous ne voulez pas spécifiquement qu'une exception soit levée dans le cas où il y a plus d'un élément, utilisez
First()
.Les deux sont efficaces, prenez le premier élément.
First()
est légèrement plus efficace car cela ne prend pas la peine de vérifier s'il y a un deuxième élément.La seule différence est qu'il
Single()
s'attend à ce qu'il n'y ait qu'un seul élément dans l'énumération pour commencer, et lève une exception s'il y en a plusieurs. Vous utilisez.Single()
si vous souhaitez spécifiquement une exception levée dans ce cas.la source
Si je me souviens, Single () vérifie s'il y a un autre élément après le premier (et lève une exception si c'est le cas), tandis que First () s'arrête après l'avoir obtenu. Les deux lèvent une exception si la séquence est vide.
Personnellement, j'utilise toujours First ().
la source
En ce qui concerne la performance: un collègue et moi discutions des performances de Single vs First (ou SingleOrDefault vs FirstOrDefault), et je plaidais pour que First (ou FirstOrDefault) soit plus rapide et améliore les performances (je suis tout à fait de faire notre application cours plus vite).
J'ai lu plusieurs articles sur Stack Overflow qui en débattent. Certains disent qu'il y a de petits gains de performances en utilisant First au lieu de Single. En effet, First renvoie simplement le premier élément tandis que Single doit analyser tous les résultats pour s'assurer qu'il n'y a pas de doublon (c'est-à-dire: s'il a trouvé l'élément dans la première ligne du tableau, il analysera toujours toutes les autres lignes pour assurez-vous qu'il n'y a pas de deuxième valeur correspondant à la condition, ce qui entraînerait une erreur). Je me sentais comme si j'étais sur un terrain solide, "First" étant plus rapide que "Single", j'ai donc décidé de le prouver et de mettre fin au débat.
J'ai configuré un test dans ma base de données et ajouté 1 000 000 de lignes d'ID UniqueIdentifier Foreign UniqueIdentifier Info nvarchar (50) (rempli de chaînes de chiffres de «0» à «999,9999»
J'ai chargé les données et défini l'ID comme champ de clé primaire.
En utilisant LinqPad, mon objectif était de montrer que si vous recherchiez une valeur sur 'Foreign' ou 'Info' en utilisant Single, ce serait bien pire que d'utiliser First.
Je ne peux pas expliquer les résultats que j'ai obtenus. Dans presque tous les cas, l'utilisation de Single ou SingleOrDefault était légèrement plus rapide. Cela n'a aucun sens logique pour moi, mais je voulais partager cela.
Ex: j'ai utilisé les requêtes suivantes:
J'ai essayé des requêtes similaires sur le champ de clé 'Étranger' qui n'était pas indexé en pensant que First serait plus rapide, mais Single était toujours légèrement plus rapide dans mes tests.
la source
Ils sont différents. Tous deux affirment que l'ensemble de résultats n'est pas vide, mais single affirme également qu'il n'y a pas plus d'un résultat. Personnellement, j'utilise Single dans les cas où je m'attends à ce qu'il n'y ait qu'un seul résultat, car le fait d'obtenir plus d'un résultat est une erreur et devrait probablement être traité comme tel.
la source
Vous pouvez essayer un exemple simple pour faire la différence. Une exception sera levée sur la ligne 3;
la source
Beaucoup de gens que je connais utilisent FirstOrDefault (), mais j'ai tendance à utiliser SingleOrDefault () davantage parce que souvent il y aurait une sorte d'incohérence des données s'il y en avait plusieurs. Il s'agit cependant de LINQ-to-Objects.
la source
Les enregistrements dans l'entité Employé:
Employeeid = 1
: Un seul employé avec cet IDFirstname = Robert
: Plus d'un employé portant ce nomEmployeeid = 10
: Aucun employé avec cet IDMaintenant, il est nécessaire de comprendre ce
Single()
queFirst()
signifie et de dire en détail.Célibataire()
Single () est utilisé pour renvoyer un seul enregistrement qui existe uniquement dans une table, donc la requête ci-dessous renverra l'employé dont
employeed =1
nous n'avons qu'un seul employé dont la valeurEmployeed
est 1. Si nous avons deux enregistrements pourEmployeeId = 1
cela, il génère une erreur (voir le erreur ci-dessous dans la deuxième requête où nous utilisons un exemple pourFirstname
.Ce qui précède renverra un seul enregistrement, qui a 1
employeeId
Ce qui précède lèvera une exception car les enregistrements multi-espaces sont dans la table pour
FirstName='Robert'
. L'exception seraCela lèvera une fois de plus une exception car aucun enregistrement n'existe pour id = 10. L'exception sera
Car
EmployeeId = 10
il renverra null, mais comme nous l'utilisons,Single()
il générera une erreur. Afin de gérer l'erreur nulle, nous devons utiliserSingleOrDefault()
.Première()
First () renvoie à partir de plusieurs enregistrements les enregistrements correspondants triés par ordre croissant en fonction de
birthdate
sorte qu'il renvoie «Robert» qui est le plus âgé.Ci-dessus devrait retourner le plus ancien, Robert selon DOB.
Ci-dessus lèvera une exception car aucun enregistrement pour id = 10 n'existe. Pour éviter une exception nulle, nous devrions utiliser
FirstOrDefault()
plutôt queFirst()
.Remarque: Nous pouvons utiliser uniquement
First()
/Single()
lorsque nous sommes absolument sûrs qu'il ne peut pas retourner une valeur nulle.Dans les deux fonctions, utilisez SingleOrDefault () OU FirstOrDefault () qui gérera une exception nulle, dans le cas où aucun enregistrement n'a été trouvé, il renverra null.
la source