175 votes

Sélectionner plusieurs enregistrements à partir d'une liste d'identifiants avec linq

J'ai une liste contenant les identités de mes clients. UserProfile table. Comment puis-je sélectionner tous les UserProfiles d'après la liste d'identités que j'ai reçue dans un var en utilisant LINQ ?

var idList = new int[1, 2, 3, 4, 5];
var userProfiles = _dataContext.UserProfile.Where(......);

J'étais coincé ici. Je peux le faire en utilisant des boucles for, etc. Mais je préfère le faire avec LINQ .

4 votes

Chercher et trouver sont deux choses différentes. Mais puisque vous pouvez regarder par-dessus mon épaule grâce à Internet, pourriez-vous me dire comment vous savez que je n'ai pas fait de recherche ? attendez, ne dites rien ! Vous l'avez vu, n'est-ce pas ? C'est exactement ce que je veux dire.

6 votes

Poser une question coûte plus de temps que de faire une recherche. la prochaine fois, supposez qu'il/elle a fait une recherche ou 10.

2 votes

Ce point fait toujours l'objet d'une attention particulière, c'est pourquoi j'ai pensé mentionner que ReSharper fait un très bon travail en suggérant des endroits où vous pouvez transformer du code itératif en déclarations LINQ. Pour les personnes qui découvrent LINQ, il peut s'agir d'un outil indispensable rien que pour cela.

283voto

Yuck Points 23174

Vous pouvez utiliser Contains() pour ça. On se sent un peu à l'envers quand on essaie vraiment de produire un IN mais cela devrait suffire :

var userProfiles = _dataContext.UserProfile
                               .Where(t => idList.Contains(t.Id));

Je suppose également que chaque UserProfile record va avoir un int Id champ. Si ce n'est pas le cas, vous devrez vous adapter en conséquence.

0 votes

Oui, les enregistrements du profil de l'utilisateur contiennent des identifiants. Donc d'une manière ou d'une autre je ferais quelque chose comme t => t.id == idList.Contains(id) ?

0 votes

Contains() s'occupera de la vérification de l'égalité pour chaque id si vous l'utilisez comme je l'ai écrit dans la réponse. Il n'est pas nécessaire d'écrire explicitement == n'importe où lorsque vous essayez de comparer les éléments d'un ensemble (le tableau) à un autre (la table de la base de données).

0 votes

Le problème est que t contient tout l'objet UserProfile, et que l'idList ne contient que des int. Le compilateur s'est plaint de quelque chose mais j'ai réussi à le résoudre. Merci de votre compréhension.

126voto

David Gregor Points 1647

La solution avec .Where et .Contains a une complexité de O(N carré). Un simple .Join devrait avoir des performances bien meilleures (proches de O(N) en raison du hachage). Donc le code correct est :

_dataContext.UserProfile.Join(idList, up => up.ID, id => id, (up, id) => up);

Et maintenant le résultat de ma mesure. J'ai généré 100 000 UserProfiles et 100 000 ids. Join a pris 32ms et .Where avec .Contains a pris 2 minutes et 19 secondes ! J'ai utilisé un IEnumerable pur pour ce test afin de prouver mon affirmation. Si vous utilisez List au lieu de IEnumerable, .Where et .Contains seront plus rapides. De toute façon la différence est significative. Le plus rapide .Where .Contains est avec Set<>. Tout dépend de la complexité des coletions sous-jacentes pour .Contains. Regardez ce poste pour apprendre la complexité de linq. Regardez mon exemple de test ci-dessous :

    private static void Main(string[] args)
    {
        var userProfiles = GenerateUserProfiles();
        var idList = GenerateIds();
        var stopWatch = new Stopwatch();
        stopWatch.Start();
        userProfiles.Join(idList, up => up.ID, id => id, (up, id) => up).ToArray();
        Console.WriteLine("Elapsed .Join time: {0}", stopWatch.Elapsed);
        stopWatch.Restart();
        userProfiles.Where(up => idList.Contains(up.ID)).ToArray();
        Console.WriteLine("Elapsed .Where .Contains time: {0}", stopWatch.Elapsed);
        Console.ReadLine();
    }

    private static IEnumerable<int> GenerateIds()
    {
       // var result = new List<int>();
        for (int i = 100000; i > 0; i--)
        {
            yield return i;
        }
    }

    private static IEnumerable<UserProfile> GenerateUserProfiles()
    {
        for (int i = 0; i < 100000; i++)
        {
            yield return new UserProfile {ID = i};
        }
    }

Sortie de console :

Temps écoulé : 00:00:00.0322546

Temps écoulé .Où .Contient : 00:02:19.4072107

4 votes

Pouvez-vous étayer vos propos par des chiffres ?

0 votes

C'est bien, mais cela me rend curieux de savoir quel serait le calendrier de la mise en œuvre de l'accord de Cotonou. List est utilisé. +1

0 votes

Ok, voici les temps qui vous intéressent : List a pris 13,1 secondes et HashSet a pris 0,7 ms ! Donc le .Where .Contains est meilleur seulement dans le cas de HashSet (quand .Contains a une complexité O(1)). Dans les autres cas, le .Join est meilleur

34voto

Tomino Points 107

De bonnes réponses, mais n'en oubliez pas une. IMPORTANT chose - ils donnent des résultats différents !

  var idList = new int[1, 2, 2, 2, 2]; // same user is selected 4 times
  var userProfiles = _dataContext.UserProfile.Where(e => idList.Contains(e)).ToList();

Cela renverra 2 lignes de la base de données (et cela pourrait être correct, si vous voulez juste une liste d'utilisateurs triée distincte).

MAIS dans de nombreux cas, vous pourriez vouloir un non trié liste des résultats. Vous devez toujours y penser comme à une requête SQL. Veuillez voir l'exemple avec le panier d'achat eshop pour illustrer ce qui se passe :

  var priceListIDs = new int[1, 2, 2, 2, 2]; // user has bought 4 times item ID 2
  var shoppingCart = _dataContext.ShoppingCart
                     .Join(priceListIDs, sc => sc.PriceListID, pli => pli, (sc, pli) => sc)
                     .ToList();

Cela donnera 5 les résultats de la DB. L'utilisation de "contains" serait erronée dans ce cas.

15voto

fabigler Points 3472

Cela devrait être simple. Essayez ceci :

var idList = new int[1, 2, 3, 4, 5];
var userProfiles = _dataContext.UserProfile.Where(e => idList.Contains(e));

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X