J'ai une liste contenant les identifiants de ma table UserProfile
. Comment puis-je sélectionner tous les UserProfiles
en fonction de la liste des identifiants que j'ai obtenus dans un var
en utilisant LINQ
?
var idList = new int[1, 2, 3, 4, 5];
var userProfiles = _dataContext.UserProfile.Where(......);
Je suis coincé ici. Je peux le faire en utilisant des boucles for etc. Mais je préférerais le faire avec LINQ
.
Vous pouvez utiliser Contains()
pour cela. Lorsque vous essayez réellement de créer une clause IN
, vous vous sentirez un peu en arrière, mais vous devriez le faire:
var userProfiles = _dataContext.UserProfile
.Where(t => idList.Contains(t.Id));
Je suppose également que chaque enregistrement UserProfile
aura un champ int
Id
. Si ce n'est pas le cas, vous devrez vous adapter en conséquence.
La solution avec .Where et .Contains a une complexité de O (N carré). Simple .Join devrait avoir de bien meilleures performances (près 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, résultat de ma mesure. J'ai généré 100 000 UserProfiles et 100 000 identifiants. Join a pris 32ms et .Where with .Contains a pris 2 minutes et 19 secondes! J'ai utilisé purement IEnumerable pour ces tests afin de prouver mon affirmation. Si vous utilisez List au lieu de IEnumerable, les champs .Where et .Contains seront plus rapides. Quoi qu'il en soit, la différence est significative. Le plus rapide .Where .Contains se trouve avec Set <>. Tout dépend de la complexité des colonnes sous-jacentes pour .Contains. Regardez cet article pour en savoir plus sur la complexité de linq.Regardez mon échantillon 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 la console:
Temps écoulé. Heure de connexion: 00: 00: 00.0322546
Elapsé .Où .Contient le temps: 00: 02: 19.4072107
Les réponses sont bonnes, mais n'oubliez pas une chose IMPORTANTE - elles 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 retournera 2 lignes de la base de données (et cela pourrait être correct si vous voulez juste une liste séparée d’utilisateurs)
MAIS Dans de nombreux cas, vous pourriez vouloir une liste non triée . Vous devez toujours y penser comme une requête SQL. Veuillez consulter 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();
Ceci renverra 5 résultats de la base de données. Utiliser 'contient' serait faux dans ce cas.
Cela devrait être simple. Essaye ça:
var idList = new int[1, 2, 3, 4, 5];
var userProfiles = _dataContext.UserProfile.Where(e => idList.Contains(e));