Code Linq pour sélectionner un élément

105

Je me surprends à écrire beaucoup de code comme celui-ci pour sélectionner un élément qui correspond

var item = (from x in Items where x.Id == 123 select x).First();

Y a-t-il une façon plus propre de le faire ou est-ce aussi concis que je vais l'être?

EDIT: J'aurais dû dire "Une manière plus propre en utilisant la syntaxe linq". J'étais déjà au courant de la syntaxe lambda et ça commence à ressembler à ce que c'est en fait le seul moyen. J'ai cependant obtenu des informations utiles, alors merci à tous ceux qui ont répondu.

Mikey Hogarth
la source
5
Personnellement j'évite Single()et SingleOrDefault()SI je sais que les données sont déjà uniques (par exemple à partir d'une base de données qui a cette contrainte, etc.), car le Single()force à scanner le reste de la liste pour trouver un éventuel doublon, mais c'est moi. Si vous devez renforcer votre caractère unique à ce stade, utilisez la Single()famille, sinon utilisez la First()famille.
James Michael Hare

Réponses:

176

Dépend de combien vous aimez la syntaxe de requête linq, vous pouvez utiliser les méthodes d'extension directement comme:

var item = Items.First(i => i.Id == 123);

Et si vous ne voulez pas lancer d'erreur si la liste est vide, utilisez FirstOrDefaultqui renvoie la valeur par défaut pour le type d'élément ( nullpour les types référence):

var item = Items.FirstOrDefault(i => i.Id == 123);

if (item != null)
{
    // found it
}

Single()et SingleOrDefault()peut également être utilisé, mais si vous lisez à partir d'une base de données ou de quelque chose qui garantit déjà l'unicité, je ne prendrais pas la peine car il doit analyser la liste pour voir s'il y a des doublons et des jets. First()et FirstOrDefault()arrêtez-vous sur le premier match, donc ils sont plus efficaces.

De la famille First()et Single(), voici où ils lancent:

  • First() - lance si vide / non trouvé, ne lance pas si dupliqué
  • FirstOrDefault() - renvoie la valeur par défaut si vide / introuvable, ne lance pas si dupliqué
  • Single() - jette si vide / non trouvé, jette si un double existe
  • SingleOrDefault() - renvoie la valeur par défaut si vide / introuvable, lance s'il existe un doublon
James Michael Hare
la source
1
Je pense qu'il vous manque deux signes égaux. Devrait êtrei.Id == 123
davehale23
18

FirstOrDefault ou SingleOrDefault peuvent être utiles, en fonction de votre scénario et du fait que vous souhaitez gérer le fait qu'il y ait zéro ou plusieurs correspondances:

FirstOrDefault: renvoie le premier élément d'une séquence, ou une valeur par défaut si aucun élément n'est trouvé.

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

Je ne sais pas comment cela fonctionne dans une requête linq 'from' mais dans la syntaxe lambda, cela ressemble à ceci:

var item1 = Items.FirstOrDefault(x => x.Id == 123);
var item2 = Items.SingleOrDefault(x => x.Id == 123);
stuartd
la source
lequel est le plus efficace en termes de temps DB? Si j'avais un varchar, je voulais une correspondance exacte mais possible qu'il n'y en ait pas?
Piotr Kula
2
La réponse acceptée résout cela dans son intégralité, mais essentiellement FirstOrDefault s'arrête dès qu'il trouve une correspondance, mais SingleOrDefault doit examiner toute la liste pour s'assurer qu'il y a exactement une correspondance.
stuartd le
12

Voici les méthodes préférées:

var item = Items.SingleOrDefault(x => x.Id == 123);

Ou

var item = Items.Single(x => x.Id == 123);
James Hill
la source
Merci - donc dans cette situation, il n'y a pas de notation linq et j'ai besoin d'utiliser des lambdas?
Mikey Hogarth
C'est plutôt bien. Je n'ai pas vraiment beaucoup utilisé linq, donc je ne suis pas sûr d'être au courant de ces méthodes.
salaire
1
La méthode unique vérifie que la valeur de retour est unique. Donc, si votre collection est volumineuse, cela peut prendre beaucoup de temps. La première méthode renvoie simplement le premier élément qui correspond au prédicat.
meziantou
La réponse de @wageoghe James n'utilise pas linq - les méthodes Single et SingleOrDefault font partie de l'implémentation IEnumerable.
Mikey Hogarth
12

Juste pour faciliter la vie de quelqu'un, la requête linq avec expression lambda

(from x in Items where x.Id == 123 select x).FirstOrDefault();

aboutit à une requête SQL contenant un select top (1).

Amal
la source
9

Cela peut mieux se résumer à ceci.

var item = Items.First(x => x.Id == 123);

Votre requête collecte actuellement tous les résultats (et il peut y en avoir plus d'un) dans l'énumérable, puis prend le premier de cet ensemble, ce qui fait plus de travail que nécessaire.

Single / SingleOrDefault valent la peine, mais uniquement si vous souhaitez parcourir toute la collection et vérifier que la correspondance est unique en plus de la sélectionner. First / FirstOrDefault prendra simplement la première correspondance et partira, quel que soit le nombre de doublons existants.

Chris Hannon
la source
4

Vous pouvez utiliser la syntaxe de la méthode d'extension:

var item = Items.Select(x => x.Id == 123).FirstOrDefault();

En dehors de cela, je ne sais pas combien vous pouvez obtenir plus concis, sans peut-être écrire vos propres méthodes d'extension spécialisées "First" et "FirstOrDefault".

Saléoghe
la source
Je ne pense pas que ce soit le comportement prévu. Cette instruction Select renvoie (en fait pas, mais elle sélectionne pour le retour) une collection de booléens, un pour chaque élément de Items, le premier de ce qui est retourné comme item, qui devient simplement un bool. Utilisez la solution de Chris Hannon
Leonardo Daga
Peut-être voulez-vous dire Wherepar opposition à Select, ce qui a déjà été dit, mais cette réponse est incorrecte. Select in c # change les résultats en IEnumerable <bool>, donc vous obtenez un boolpour le premier élémentx.Id == 123
bradlis7
2

Je vais vous dire ce qui a fonctionné pour moi:

int id = int.Parse(insertItem.OwnerTableView.DataKeyValues[insertItem.ItemIndex]["id_usuario"].ToString());

var query = user.First(x => x.id_usuario == id);
tbUsername.Text = query.username;
tbEmail.Text = query.email;
tbPassword.Text = query.password;

Mon identifiant est la ligne que je veux interroger, dans ce cas je l'ai obtenu à partir d'un radGrid, puis je l'ai utilisé pour interroger, mais cette requête renvoie une ligne, vous pouvez alors attribuer les valeurs que vous avez obtenues de la requête à la zone de texte, ou quoi que ce soit , J'ai dû les affecter à textbox.

G Jeny Ramirez
la source