Linq ke SQL bagaimana melakukan "di mana [kolom] di (daftar nilai)"

101

Saya memiliki fungsi di mana saya mendapatkan daftar id, dan saya perlu mengembalikan daftar yang cocok dengan deskripsi yang terkait dengan id. Misalnya:

public class CodeData
{
    string CodeId {get; set;}
    string Description {get; set;}
}

public List<CodeData> GetCodeDescriptionList(List<string> codeIDs)
    //Given the list of institution codes, return a list of CodeData
    //having the given CodeIds
}

Jadi jika saya membuat sql untuk ini sendiri, saya hanya akan melakukan sesuatu seperti berikut (di mana klausa in berisi semua nilai dalam argumen codeIds):

Select CodeId, Description FROM CodeTable WHERE CodeId IN ('1a','2b','3')

Dalam Linq ke Sql saya tidak bisa menemukan padanan dari klausa "IN". Yang terbaik yang saya temukan sejauh ini (yang tidak berfungsi) adalah:

 var foo = from codeData in channel.AsQueryable<CodeData>()
           where codeData.CodeId == "1" || codeData.CodeId == "2"
           select codeData;

Masalahnya, saya tidak dapat secara dinamis menghasilkan daftar klausa "ATAU" untuk linq ke sql, karena mereka disetel pada waktu kompilasi.

Bagaimana seseorang mencapai klausa where yang memeriksa kolom dalam daftar nilai dinamis menggunakan Linq ke Sql?

Nathan
sumber

Jawaban:

159

Menggunakan

where list.Contains(item.Property)

Atau dalam kasus Anda:

var foo = from codeData in channel.AsQueryable<CodeData>()
          where codeIDs.Contains(codeData.CodeId)
          select codeData;

Tetapi Anda mungkin juga melakukannya dalam notasi titik:

var foo = channel.AsQueryable<CodeData>()
                 .Where(codeData => codeIDs.Contains(codeData.CodeId));
Jon Skeet
sumber
bagaimana cara menggunakannya jika CodeId adalah Integer ??
Kiran Solkar
2
@KiranSolkar: Kalau begitu kiranya codeIDsa List<int>, dan semua akan baik-baik saja.
Jon Skeet
@ JonSkeet Bukankah itu case sensitive? Jika codeID adalah daftar string huruf besar dan codeData.codeId adalah string huruf kecil, itu akan gagal.
PersyJack
@PersyJack: Tidak ada dalam pertanyaan tentang hal itu harus peka huruf besar / kecil. Mengenai apakah itu akan terjadi atau tidak, saya tidak ingat apakah LINQ ke SQL memberlakukan sensitivitas huruf secara default atau membiarkan pengaturan db mengaturnya.
Jon Skeet
1
@PersyJack LINQ ke SQL menghasilkan kueri T-SQL, yang kemudian berjalan di SQL Server menggunakan pengaturan database untuk sensitivitas huruf. Meskipun, jika seseorang tidak berhati-hati, dan mewujudkan hasil kueri, sebelum menerapkan LINQ ke objek dalam memori, mereka mungkin mengalami konsekuensi dari sensitivitas huruf yang tidak cocok.
Zarepheth
26

Anda juga bisa menggunakan:

List<int> codes = new List<int>();

codes.add(1);
codes.add(2);

var foo = from codeData in channel.AsQueryable<CodeData>()
          where codes.Any(code => codeData.CodeID.Equals(code))
          select codeData;
Nick DeMayo
sumber
1
Saya harus menggunakan ini karena penerapan IQToolkit kami tidak mendukung. Berisi ()
DJ van Wyk
1

Saya telah menggunakan metode dalam jawaban Jon Skeet, tetapi metode lain terpikir oleh saya Concat. The Concatmetode yang dilakukan sedikit lebih baik dalam tes terbatas, tapi itu merepotkan dan saya mungkin akan hanya dengan tongkat Contains, atau mungkin aku akan menulis metode pembantu untuk melakukan hal ini untuk saya. Apa pun pilihannya, berikut opsi lain jika ada yang tertarik:

Metode

// Given an array of id's
var ids = new Guid[] { ... };

// and a DataContext
var dc = new MyDataContext();

// start the queryable
var query = (
    from thing in dc.Things
    where thing.Id == ids[ 0 ]
    select thing 
);

// then, for each other id
for( var i = 1; i < ids.Count(); i++ ) {
    // select that thing and concat to queryable
    query.Concat(
        from thing in dc.Things
        where thing.Id == ids[ i ]
        select thing
    );
}

Uji kinerja

Ini sama sekali tidak ilmiah. Saya membayangkan struktur database Anda dan jumlah ID yang terlibat dalam daftar akan berdampak signifikan.

Saya menyiapkan tes di mana saya melakukan 100 percobaan masing-masing Concatdan di Containsmana setiap percobaan melibatkan pemilihan 25 baris yang ditentukan oleh daftar kunci primer secara acak. Saya telah menjalankan ini sekitar belasan kali, dan sering kali Concatmetode ini keluar 5 - 10% lebih cepat, meskipun satu kali Containsmetode dimenangkan hanya dengan sedikit.

DCShannon
sumber
0
 var filterTransNos = (from so in db.SalesOrderDetails
                    where  ItemDescription.Contains(ItemDescription)
                            select new { so.TransNo }).AsEnumerable();    


listreceipt = listreceipt.Where(p => filterTransNos.Any(p2 => p2.TransNo == p.TransNo)).ToList();
Deepan Raj
sumber
-1

Inilah cara saya melakukannya dengan menggunakan HashSet

        HashSet<String> hs = new HashSet<string>(new String[] { "Pluto", "Earth", "Neptune" });
        String[] arr =
        {
            "Pluto",
            "Earth",
            "Neptune",
            "Jupiter",
            "Saturn",
            "Mercury",
            "Pluto",
            "Earth",
            "Neptune",
            "Jupiter",
            "Saturn",
            "Mercury",
            // etc.
        };
        ICollection<String> coll = arr;

        String[] arrStrFiltered = coll.Where(str => hs.Contains(str)).ToArray();

HashSet pada dasarnya hampir ke O (1) jadi kompleksitas Anda tetap O (n).

MG
sumber
Ini tentang LINQ-to-SQL. Pertimbangan LINQ-ke-objek seperti itu tidak berlaku.
Gert Arnold
ICollection dapat berasal dari LINQ-SQL juga, ini adalah cara umum
MG
Pertanyaannya adalah tentang bagaimana membangun ekspresi yang diterjemahkan ke dalam SQL yang benar. Itu tidak ada hubungannya dengan cara mencari koleksi lokal. Jawaban Anda hanya akan menipu pembaca di masa mendatang yang tidak menyadari perbedaan ini.
Gert Arnold