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?
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é.
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
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.
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.
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
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.
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. :)
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é.
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...
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();
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);
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.
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.