Linq ile Id'lerin listesine göre birden fazla kayıt seçin


122

Masamın kimliklerini içeren bir listem var UserProfile. Nasıl bütün seçebilirsiniz UserProfilesi var Id en listesinde dayalı varkullanarak LINQ?

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

Burada sıkışıp kaldım. Bunu döngüler vb. Kullanarak yapabilirim. Ama bunu ile yapmayı tercih ederim LINQ.


4
arama ve bulma 2 farklı şeydir. Ama internetten omzumun üzerinden bakabildiğine göre, aramadığımı nasıl bildiğini söyleyebilir misin? bekle söyleme! Doğru gördün mü? tam olarak benim açımdan.
Yustme

5
Bir soru sormak, arama yapmaktan daha fazla zaman alır. bir dahaki sefere bir arama yaptığını varsayalım veya 10.
Yustme

2
Bu hala oldukça dikkat çekiyor, bu yüzden ReSharper'ın yinelemeli kodu LINQ ifadelerine dönüştürebileceğiniz yerler önerme konusunda çok iyi bir iş çıkardığından bahsedeceğimi düşündüm. LINQ'da yeni olan insanlar için, bu amaç için tek başına sahip olmak vazgeçilmez bir araç olabilir.
Yuck

Yanıtlar:


206

Kullanabilirsiniz Contains() . Gerçekten bir INcümle üretmeye çalıştığınızda biraz geriye doğru hissedeceksiniz , ancak bunu yapmalı:

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

Ayrıca her UserProfilekaydın bir int Idalanı olacağını varsayıyorum . Durum bu değilse, buna göre ayarlamanız gerekir.


Merhaba, evet userprofile kayıtları id'leri içeriyor. Yani bir şekilde t => t.id == idList.Contains (id) gibi bir şey yapıyor olurdum?
Yustme

Contains()idCevapta yazdığım gibi kullanırsanız , her bir değerde bu eşitlik kontrolünü halledecektir . ==Bir kümenin (dizi) öğelerini diğeriyle (veritabanı tablosu) karşılaştırmaya çalışırken açıkça herhangi bir yere yazmanız gerekmez .
Yuck

Sorun şu ki, t UserProfile nesnesinin tamamını tutuyor ve idList yalnızca int'leri içeriyor. Derleyici bir şeyden şikayet etti ama ben düzeltmeyi başardım. Teşekkürler.
Yustme

2
@Yuck - Benim için çalışmıyor, İşlev zaman aşımına uğradı diyor! Tembel yüklemeyi devre dışı bıraktınız ancak yine de başarısız oluyor.
bhuvin

1
"Lambda ifadesi 'int' türüne dönüştürülemiyor çünkü bu bir temsilci türü değil". Bunu nasıl düzeltebilirim?
Stian

92

.Where ve .Contains ile çözüm O (N kare) karmaşıklığına sahiptir. Basit .Join çok daha iyi performansa sahip olmalıdır (hashing nedeniyle O (N) değerine yakın). Yani doğru kod:

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

Ve şimdi ölçümümün sonucu. 100.000 Kullanıcı Profili ve 100.000 kimlik oluşturdum. Birleştirme 32 ms sürdü ve .Where ile .Contains 2 dakika 19 saniye sürdü! İfademi kanıtlamak için bu test için saf IEnumerable kullandım. IEnumerable yerine List kullanırsanız, .Where ve .Contains daha hızlı olacaktır. Her neyse, fark önemli. En hızlı .Where .Contains <> Set ile olur. Tüm bunlar .Contains için temel alınan seçmelerin karmaşıklığına bağlıdır. Linq karmaşıklığı hakkında bilgi edinmek için bu gönderiye bakın. Aşağıdaki test örneğime bakın:

    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};
        }
    }

Konsol çıkışı:

Geçen. Katılma süresi: 00: 00: 00.0322546

Geçen. Nerede. Zaman içerir: 00: 02: 19.4072107


4
Bunu rakamlarla destekleyebilir misin?
Yustme

Güzel, ancak Listkullanıldığında zamanlamaların ne olacağını merak etmeme neden oluyor . +1
Yustme

Tamam, işte ilgilendiğiniz zamanlamalar: Liste 13.1 saniye sürdü ve HashSet 0,7 ms sürdü! Yani .Where .Contains yalnızca HashSet durumunda en iyisidir (.Contains O (1) karmaşıklığına sahip olduğunda). Diğer durumlarda, .Join daha iyidir
David Gregor

5
Ben olsun Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator.LINQ2SQL datacontext kullanırken hata.
Mayank Raichura

3
@Yustme - performans her zaman önemlidir. ("Bu kabul edilen cevap olmalı" adam
olmaktan

19

Güzel cevaplar, ancak ÖNEMLİ bir şeyi unutmayın - farklı sonuçlar sağlarlar!

  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();

Bu, DB'den 2 satır döndürecektir (ve yalnızca farklı sıralı bir kullanıcı listesi istiyorsanız bu doğru olabilir)

ANCAK çoğu durumda, sıralanmamış bir sonuç listesi isteyebilirsiniz . Her zaman bir SQL sorgusu gibi düşünmeniz gerekir. Neler olduğunu göstermek için lütfen eshop alışveriş sepeti örneğine bakın:

  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();

Bu, DB'den 5 sonuç döndürecektir . Bu durumda 'içerir' kullanmak yanlış olur.


13

Bu basit olmalı. Bunu dene:

var idList = new int[1, 2, 3, 4, 5];
var userProfiles = _dataContext.UserProfile.Where(e => idList.Contains(e));
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.