Bagaimana cara bergabung di LINQ pada banyak bidang dalam satu gabungan


244

Saya perlu melakukan kueri LINQ2DataSet yang melakukan gabungan pada lebih dari satu bidang (sebagai

var result = from x in entity
join y in entity2 
       on x.field1 = y.field1 
and 
          x.field2 = y.field2

Saya belum menemukan solusi yang sesuai (saya bisa menambahkan batasan ekstra ke klausa mana, tapi ini jauh dari solusi yang cocok, atau menggunakan solusi ini , tetapi mengasumsikan equijoin).

Apakah mungkin di LINQ untuk bergabung di beberapa bidang dalam satu gabungan?

EDIT

var result = from x in entity
             join y in entity2
             on new { x.field1, x.field2 } equals new { y.field1, y.field2 }

adalah solusi yang saya referensikan dengan mengasumsikan equijoin di atas.

EDIT selanjutnya

Untuk menjawab kritik bahwa contoh asli saya adalah equijoin, saya mengakui bahwa, Persyaratan saya saat ini adalah untuk equijoin dan saya sudah menggunakan solusi yang saya rujuk di atas.

Namun, saya mencoba memahami kemungkinan dan praktik terbaik apa yang saya / harus saya pakai dengan LINQ. Saya akan perlu melakukan permintaan rentang Tanggal bergabung dengan ID tabel segera, dan baru saja mengatasi masalah itu, Sepertinya saya harus menambahkan rentang tanggal dalam klausa di mana.

Terima kasih, seperti biasa, untuk semua saran dan komentar yang diberikan


48
Hanya FYI bagi siapa saja yang membaca ini, jika Anda melakukan multi bidang bergabung dalam kelas annon, Anda HARUS memberi nama bidang di kedua kelas annon yang sama, jika tidak, Anda mendapatkan kesalahan kompilasi.
Markus

6
Atau lebih tepatnya, Anda harus memastikan mereka memiliki nama yang cocok. yaitu Anda bisa memberi nama bidang dari salah satu jenis anon untuk membuatnya cocok dengan yang lainnya.
Tom Ferguson

1
Perhatikan jawaban ini stackoverflow.com/a/34176502/1704458
TS

Saya menggunakan tupel untuk kedua sisi sama dengan bukan objek dan sepertinya berfungsi juga.
GHZ

Jawaban:


89

Solusi dengan tipe anonim seharusnya bekerja dengan baik. LINQ hanya bisa mewakili equijoins (dengan klausa join, sih), dan memang itulah yang Anda katakan ingin Anda ekspresikan berdasarkan permintaan awal Anda.

Jika Anda tidak menyukai versi dengan jenis anonim karena alasan tertentu, Anda harus menjelaskan alasan itu.

Jika Anda ingin melakukan sesuatu selain dari apa yang semula Anda minta, tolong berikan contoh apa yang benar - benar ingin Anda lakukan.

EDIT: Menanggapi hasil edit dalam pertanyaan: ya, untuk melakukan "rentang tanggal" bergabung, Anda perlu menggunakan klausa tempat. Mereka benar-benar setara secara semantik, jadi itu hanya masalah optimisasi yang tersedia. Equijoins memberikan optimasi sederhana (dalam LINQ ke Objek, yang mencakup LINQ ke DataSet) dengan membuat pencarian berdasarkan urutan dalam - anggap sebagai hashtable dari kunci ke urutan entri yang cocok dengan kunci itu.

Melakukannya dengan rentang tanggal agak sulit. Namun, tergantung pada apa yang Anda maksud dengan "rentang tanggal bergabung" Anda mungkin dapat melakukan sesuatu yang serupa - jika Anda berencana membuat "band" tanggal (misalnya satu per tahun) sedemikian rupa sehingga dua entri yang terjadi di tahun yang sama (tetapi tidak pada tanggal yang sama) harus cocok, maka Anda dapat melakukannya hanya dengan menggunakan band itu sebagai kuncinya. Jika lebih rumit, misalnya satu sisi gabungan menyediakan rentang, dan sisi lain gabungan menyediakan satu tanggal, cocok jika itu berada dalam rentang itu, yang akan lebih baik ditangani dengan whereklausa (setelah satu detikfromklausa) IMO. Anda dapat melakukan sihir yang sangat funky dengan memesan satu sisi atau yang lain untuk menemukan kecocokan yang lebih efisien, tetapi itu akan banyak pekerjaan - saya hanya akan melakukan hal semacam itu setelah memeriksa apakah kinerja merupakan masalah.


Terima kasih, ya kinerja adalah kekhawatiran utama saya dengan menggunakan klausa where. Saya menduga klausa di mana setelah bergabung akan melakukan filter pada dataset yang lebih besar yang bisa dikurangi dengan memperkenalkan parameter bergabung kedua. Saya suka ide memesan untuk menguji apakah saya bisa mendapatkan keuntungan efisiensi
johnc

Berapa banyak catatan yang akan Anda miliki? Jangan lupa bahwa memesan hasil untuk memulai akan memakan waktu tertentu untuk memulai ...
Jon Skeet

"Mereka benar-benar setara secara semantik" - apakah kita perlu kata 'benar-benar' di sana? Mungkin maksud Anda, "Mereka benar - benar setara secara semantik" :)
onedaywhen

136
var result = from x in entity
   join y in entity2 on new { x.field1, x.field2 } equals new { y.field1, y.field2 }

Inilah yang saya cari karena 101 Linq Sampel tidak memiliki ini, atau setidaknya yang saya lihat.
Chris Marisic

1
@PeterX memang bisa, lihat jawaban saya di sini: stackoverflow.com/a/22176658/595157
niieani

13
Kode di atas tidak berfungsi. Setelah menambahkan on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 } Ini berhasil
Ravi Ram

@Ravi Ram .. Terima kasih .. komentar Anda membantu
NMathur

80
var result = from x in entity1
             join y in entity2
             on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }

Anda perlu melakukan ini, jika nama kolom berbeda dalam dua entitas.


6
Terima kasih telah menyebutkan nama kolom yang berbeda. Ini memperbaiki ekspresi buruk saya.
Gaʀʀʏ

1
Ini juga berhasil bagi saya. Jika nama kolom tidak cocok, Anda akan mendapatkan kesalahan ini, "Jenis salah satu ekspresi dalam klausa gabungan tidak benar. Ketik inferensi gagal dalam panggilan ke 'GroupJoin'."
humbads

Terima kasih telah menyebutkan variabel kunci.
Thomas.Benz

Saya mendapatkan kesalahan yang disebutkan @humbads ketika saya tidak menyebutkan semua properti int 'new {}'. Jadi hanya fyi jika Anda menyebutkan satu, Anda juga harus menyebutkan sisanya.
Ethan Melamed

Terima kasih banyak
Charly H

51

Hanya untuk melengkapinya dengan sintaksis rantai metode yang setara:

entity.Join(entity2, x => new {x.Field1, x.Field2},
                     y => new {y.Field1, y.Field2}, (x, y) => x);

Sedangkan argumen terakhir (x, y) => xadalah apa yang Anda pilih (dalam kasus di atas kami pilih x).


31

Saya pikir pilihan yang lebih mudah dibaca dan fleksibel adalah dengan menggunakan fungsi Where:

var result = from x in entity1
             from y in entity2
                 .Where(y => y.field1 == x.field1 && y.field2 == x.field2)

Ini juga memungkinkan untuk dengan mudah mengubah dari join dalam ke join kiri dengan menambahkan .DefaultIfEmpty ().


Sebagai pengguna lambda lama sekarang (sebagai lawan dari ketika saya mengajukan pertanyaan), saya harus setuju
johnc

Apakah ini lebih lambat?
AlfredBr

1
Saya pikir itu harus memiliki kinerja yang sama dengan { ... } equals new { ... }sintaks baru . LinqPad adalah alat yang hebat untuk melihat bagaimana ekspresi berperilaku (skrip SQL jika LINQ2SQL digunakan, pohon ekspresi dll.)
Alexei

Sejauh yang saya perhatikan itu memproduksi CROSS JOIN bukannya INNER JOIN
Mariusz

@Mariusz Ya, masuk akal untuk menghasilkan CROSS JOIN + WHERE daripada INNER JOIN. Untuk pertanyaan sederhana saya berharap penganalisa untuk menghasilkan yang sangat mirip.
Alexei

10
var result = from x in entity
             join y in entity2
             on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }
             select new 
             {
               /// Columns
              };

8

Anda dapat melakukan sesuatu seperti (di bawah)

var query = from p in context.T1

        join q in context.T2

        on

        new { p.Col1, p.Col2 }

        equals

         new { q.Col1, q.Col2 }

        select new {p...., q......};

Seperti yang saya sebutkan dalam pertanyaan, yang membutuhkan equijoin
johnc

7

Menggunakan operator gabungan, Anda hanya dapat melakukan equijoins. Gabungan jenis lain dapat dibangun menggunakan operator lain. Saya tidak yakin apakah gabungan persis yang Anda coba lakukan akan lebih mudah menggunakan metode ini atau dengan mengubah klausa di mana. Dokumentasi pada klausa gabungan dapat ditemukan di sini . MSDN memiliki artikel tentang operasi gabungan dengan banyak tautan ke contoh dari gabungan lain, juga.


3

Jika nama bidang berbeda dalam entitas

var result = from x in entity
   join y in entity2 on 
          new {
                field1=   x.field1,
               field2 =  x.field2 
             } 
          equals
         new { 
                field1= y.field1,
                field2=  y.myfield
              }
select new {x,y});

Terima kasih. Pencocokan nama adalah bagian yang saya lewatkan.
Brett

2

Sebagai rantai metode lengkap yang akan terlihat seperti ini:

lista.SelectMany(a => listb.Where(xi => b.Id == a.Id && b.Total != a.Total),
                (a, b) => new ResultItem
                {
                    Id = a.Id,
                    ATotal = a.Total,
                    BTotal = b.Total
                }).ToList();

-2
from d in db.CourseDispatches
                             join du in db.DispatchUsers on d.id equals du.dispatch_id
                             join u in db.Users on du.user_id equals u.id
                             join fr in db.Forumreports on (d.course_id + '_' + du.user_id)  equals  (fr.course_id + '_'+ fr.uid)

ini bekerja untuk saya


Ini untuk multiple join, dia ingin membuat join dengan banyak field dalam satu join
theLaw

-3

Deklarasikan Kelas (Jenis) untuk menampung elemen yang ingin Anda gabungkan. Dalam contoh di bawah ini menyatakan JoinElement

 public class **JoinElement**
{
    public int? Id { get; set; }
    public string Name { get; set; }

}

results = from course in courseQueryable.AsQueryable()
                  join agency in agencyQueryable.AsQueryable()
                   on new **JoinElement**() { Id = course.CourseAgencyId, Name = course.CourseDeveloper } 
                   equals new **JoinElement**() { Id = agency.CourseAgencyId, Name = "D" } into temp1

1
Ini sudah dijawab 9 tahun yang lalu ... apa nilai yang dibawa jawaban ini?
Maciej Jureczko
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.