LINQ: quand utiliser SingleOrDefault vs. FirstOrDefault() avec des critères de filtrage

Considérez les méthodes d'extension IEnumerable SingleOrDefault() et FirstOrDefault()

MSDN documente que SingleOrDefault:

Renvoie le seul élément d'une séquence, ou une valeur par défaut si la séquence est vide; cette méthode lève une exception s'il y a plus d'un élément dans la séquence.

Considérant ce qui suit FirstOrDefault de MSDN (probablement lors de l'utilisation d'un OrderBy() ou OrderByDescending() ou Aucun du tout),

Renvoie le premier élément d'un séquence

Considérons une poignée d'exemples de requêtes, il n'est pas toujours clair quand utiliser ces deux méthodes:

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?

Question

Quelles conventions de vous suivre ou de suggérer lorsque vous décidez d'utiliser SingleOrDefault() et FirstOrDefault() dans vos requêtes LINQ?

409
demandé sur p.campbell 2009-11-17 02:59:33

13 réponses

Chaque fois que vous utilisez SingleOrDefault, vous indiquez clairement que la requête doit aboutir à un résultat unique. D'un autre côté, lorsque FirstOrDefault est utilisé, la requête peut renvoyer n'importe quelle quantité de résultats mais vous indiquez que vous ne voulez que le premier.

Personnellement, je trouve la sémantique très différents et l'utilisation appropriée, en fonction des résultats attendus, améliore la lisibilité.

376
répondu Bryan Menard 2009-11-17 00:04:24

Si votre jeu de résultats renvoie 0 enregistrements:

  • SingleOrDefault renvoie la valeur par défaut pour le type (par exemple, la valeur par défaut pour int est 0)
  • FirstOrDefault renvoie la valeur par défaut pour le type

Si votre jeu de résultats renvoie 1 Enregistrement:

  • SingleOrDefault renvoie cet enregistrement
  • FirstOrDefault renvoie cet enregistrement

Si votre jeu de résultats renvoie plusieurs enregistrements:

  • SingleOrDefault lève une exception
  • FirstOrDefault renvoie le premier enregistrement

Conclusion:

Si vous voulez qu'une exception soit levée si le jeu de résultats contient de nombreux enregistrements, utilisez SingleOrDefault.

Si vous voulez toujours 1 Enregistrement quel que soit le jeu de résultats, utilisez FirstOrDefault

491
répondu Alex 2014-02-04 04:03:24

Il y a

  • une différence sémantique
  • une différence de performance

Entre les deux.

Différence Sémantique:

  • FirstOrDefault renvoie un premier élément potentiellement multiple (ou par défaut si aucun n'existe).
  • SingleOrDefault suppose qu'il y a un seul élément et le renvoie (ou par défaut si aucun n'existe). Plusieurs éléments sont une violation du contrat, une exception est levée.

Performance Différence

  • FirstOrDefault est généralement plus rapide, il itère jusqu'à ce qu'il trouve l'élément et n'a qu'à itérer l'ensemble énumérable quand il ne le trouve pas. Dans de nombreux cas, il ya une forte probabilité de trouver un article.

  • SingleOrDefault doit vérifier s'il n'y a qu'un seul élément et donc itère toujours l'ensemble énumérable. Pour être précis, il itère jusqu'à ce qu'il trouve un deuxième élément et lève une exception. Mais dans la plupart des cas, il n'y a pas de seconde élément.

Conclusion

  • Utilisez FirstOrDefault si vous ne vous souciez pas du nombre d'éléments ou lorsque vous ne pouvez pas vous permettre de vérifier l'unicité (par exemple dans une très grande collection). Lorsque vous vérifiez l'unicité lors de l'ajout des éléments à la collection, il peut être trop coûteux de le vérifier à nouveau lors de la recherche de ces éléments.

  • Utilisez SingleOrDefault si vous ne devez pas trop vous soucier des performances et que vous voulez vous assurer que l'hypothèse un seul point est clair pour le lecteur et vérifié au moment de l'exécution.

, Dans la pratique, vous utilisez First / FirstOrDefault souvent, même dans les cas où vous assumez un seul élément, pour améliorer les performances. Tu devrais toujours t'en souvenir Single / SingleOrDefault peut améliorer la lisibilité (car il énonce l'hypothèse d'un seul élément) et la stabilité (car il le vérifie) et l'utiliser de manière appropriée.

214
répondu Stefan Steinegger 2013-09-19 09:33:42

Personne n'a mentionné que FirstOrDefault traduit en SQL fait TOP 1 enregistrement, et SingleOrDefault fait TOP 2, car il a besoin de savoir s'il y a plus de 1 Enregistrement.

61
répondu shalke 2011-09-06 12:55:00

Pour LINQ - > SQL:

SingleOrDefault

  • va générer une requête comme "select * from users where userid = 1"
  • Sélectionnez l'enregistrement correspondant, lève l'exception si plusieurs enregistrements ont été trouvés
  • Utilisez si vous récupérez des données basées sur la colonne clé primaire/unique

FirstOrDefault

  • va générer une requête comme "select top 1 * from users where userid = 1"
  • Sélectionnez les premières lignes correspondantes
  • utiliser si vous récupérez des données basées sur une colonne de clé non primaire/unique
9
répondu Prakash 2018-04-04 13:35:16

J'utilise SingleOrDefault dans les situations où ma logique dicte que le sera zéro ou un résultat. S'il y en a plus, c'est une situation d'erreur, ce qui est utile.

7
répondu spender 2009-11-17 00:05:25

SingleOrDefault: vous dites que "tout au plus" il y a un élément correspondant à la requête ou à la valeur par défaut FirstOrDefault: vous dites qu'il y a "au moins" un élément correspondant à la requête ou à la valeur par défaut

Dites - le à haute voix la prochaine fois que vous devrez choisir et vous choisirez probablement judicieusement. :)

5
répondu Steven 2010-09-01 16:22:34

Dans vos cas, j'utiliserais ce qui suit:

Select by ID = = 5: il est correct d'utiliser SingleOrDefault ici, parce que vous attendez une entité [ou aucune], Si vous avez plus d'une entité avec L'ID 5, Il y a quelque chose de mal et certainement une exception digne.

Lors de la recherche de personnes dont le prénom est égal à "Bobby", il peut y en avoir plus d'un (très probablement je pense), donc vous ne devriez ni utiliser Single ni First, il suffit de sélectionner avec L'opération Where (si "Bobby" renvoie trop entités, l'utilisateur doit affiner sa recherche ou choisir l'un des résultats renvoyés)

L'ordre Par date de création doit également être effectué avec une opération Where (peu probable d'avoir une seule entité, le tri ne serait pas d'une grande utilité ;) cela implique cependant que vous voulez que toutes les entités soient triées - si vous en voulez une seule, utilisez FirstOrDefault, Single lancerait chaque fois si vous avez plus d'une entité.

4
répondu Olli 2009-11-17 00:12:07

Dans votre dernier exemple:

var latestCust = db.Customers
.OrderByDescending(x=> x.CreatedOn)
.FirstOrDefault();//Single or First, or doesn't matter?

Oui. Si vous essayez d'utiliser SingleOrDefault() et que la requête aboutit à plus d'enregistrement, vous obtiendrez et exception. La seule fois où vous pouvez utiliser SingleOrDefault() en toute sécurité est lorsque vous attendez seulement 1 et seulement 1 résultat...

3
répondu bytebender 2009-11-17 00:07:35

Les deux sont les opérateurs d'éléments et ils sont utilisés pour sélectionner un seul élément dans une séquence. Mais il y a une différence mineure entre eux. L'opérateur SingleOrDefault() lancerait une exception si plus d'un élément est satisfait à la condition où as FirstOrDefault() ne lancera aucune exception pour le même. Voici l'exemple.

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();
3
répondu Sheo Dayal Singh 2016-12-20 08:34:52

Donc, si je comprends bien maintenant, SingleOrDefault sera bon si vous interrogez des données qui sont garanties pour être uniques, c'est-à-dire appliquées par des contraintes de base de données comme la clé primaire.

Ou Existe-t-il une meilleure façon d'interroger la clé primaire.

En supposant que mon TableAcc a

AccountNumber - Primary Key, integer
AccountName
AccountOpenedDate
AccountIsActive
etc.

Et je veux interroger un AccountNumber 987654, j'utilise

var data = datacontext.TableAcc.FirstOrDefault(obj => obj.AccountNumber == 987654);
1
répondu M G 2015-04-05 14:05:42

Une chose qui est manquée dans les réponses....

S'il y a plusieurs résultats, FirstOrDefault sans order by peut ramener différents résultats en fonction de la stratégie d'index utilisée par le serveur.

Personnellement, je ne supporte pas de voir FirstOrDefault dans le code parce que pour moi, il dit que le développeur ne se souciait pas des résultats. Avec un order by bien que cela puisse être utile comme moyen d'appliquer le dernier/le plus tôt. J'ai dû corriger beaucoup de problèmes causés par développeurs négligents utilisant FirstOrDefault.

0
répondu El Dude 2015-03-11 22:39:17

Je ne comprends pas pourquoi vous utilisez FirstOrDefault(x=> x.ID == key) alors que cela pourrait récupérer les résultats beaucoup plus rapidement si vous utilisez Find(key). Si vous interrogez avec la clé Primaire de la table, la règle de base est de toujours utiliser Find(key). FirstOrDefault devrait être utilisé pour des choses de prédicat comme (x=> x.Username == username) etc.

Cela ne méritait pas de downvote car l'en-tête de la question n'était pas spécifique à linq on DB ou Linq to List/IEnumerable etc.

-7
répondu Theron Govender 2016-02-29 09:32:40