Bergabung / Dimana dengan LINQ dan Lambda

458

Saya mengalami masalah dengan kueri yang ditulis dalam LINQ dan Lambda. Sejauh ini, saya mendapatkan banyak kesalahan, inilah kode saya:

int id = 1;
var query = database.Posts.Join(database.Post_Metas,
                                post => database.Posts.Where(x => x.ID == id),
                                meta => database.Post_Metas.Where(x => x.Post_ID == id),
                                (post, meta) => new { Post = post, Meta = meta });

Saya baru menggunakan LINQ, jadi saya tidak yakin apakah kueri ini benar.

David
sumber
11
apa yang ingin kamu capai?
Germán Rodríguez
4
apa yang Anda ingin kueri lakukan dalam sebuah kalimat?
pemburu
6
Penyeleksi kunci Anda yang cara terlalu rumit. Jika Anda ingin memilih berdasarkan id, cukup x => x.ID baik-baik saja.
Eric Lippert
1
Saya ingin mendapatkan posting dari database dan data meta untuk posting itu.
David

Jawaban:

1057

Saya menemukan bahwa jika Anda terbiasa dengan sintaks SQL, menggunakan sintaks query LINQ jauh lebih jelas, lebih alami, dan membuatnya lebih mudah untuk menemukan kesalahan:

var id = 1;
var query =
   from post in database.Posts
   join meta in database.Post_Metas on post.ID equals meta.Post_ID
   where post.ID == id
   select new { Post = post, Meta = meta };

Jika Anda benar-benar terjebak dalam menggunakan lambdas, sintaks Anda agak tidak aktif. Inilah permintaan yang sama, menggunakan metode ekstensi LINQ:

var id = 1;
var query = database.Posts    // your starting point - table in the "from" statement
   .Join(database.Post_Metas, // the source table of the inner join
      post => post.ID,        // Select the primary key (the first part of the "on" clause in an sql "join" statement)
      meta => meta.Post_ID,   // Select the foreign key (the second part of the "on" clause)
      (post, meta) => new { Post = post, Meta = meta }) // selection
   .Where(postAndMeta => postAndMeta.Post.ID == id);    // where statement
Daniel Schaffer
sumber
10
@Emanuele Greco, mengenai hasil edit Anda, "Kesetaraan bidang ID diatur dalam kondisi BERGABUNG; Anda tidak perlu menggunakan klausa WHERE!": Klausa WHERE tidak menguji kesetaraan antara bidang ID, ini menguji kesetaraan antara ID posting kolom dan parameter id dinyatakan di luar kueri.
Daniel Schaffer
9
Sepotong mengagumkan lambdadan adalah kutipan mudah digunakan dan dipahami
Piotr Kula
1
contoh yang luar biasa
mainan
1
Terkadang penjelasan lambda ditulis dalam lambda. Dijelaskan dengan baik.
Pinch
80

Anda bisa menggunakan dua cara dengan ini. Menggunakan LINQPad (tidak ternilai jika Anda baru mengenal LINQ) dan database dummy, saya membuat pertanyaan berikut:

Posts.Join(
    Post_metas,
    post => post.Post_id,
    meta => meta.Post_id,
    (post, meta) => new { Post = post, Meta = meta }
)

atau

from p in Posts
join pm in Post_metas on p.Post_id equals pm.Post_id
select new { Post = p, Meta = pm }

Dalam kasus khusus ini, saya pikir sintaks LINQ lebih bersih (saya mengubah keduanya tergantung yang paling mudah dibaca).

Hal yang ingin saya tunjukkan adalah bahwa jika Anda memiliki kunci asing yang sesuai dalam database Anda, (antara post dan post_meta) maka Anda mungkin tidak perlu bergabung secara eksplisit kecuali Anda mencoba memuat sejumlah besar catatan . Contoh Anda tampaknya menunjukkan bahwa Anda mencoba memuat satu posting dan itu adalah data meta. Dengan asumsi bahwa ada banyak catatan post_meta untuk setiap posting, maka Anda dapat melakukan hal berikut:

var post = Posts.Single(p => p.ID == 1);
var metas = post.Post_metas.ToList();

Jika Anda ingin menghindari masalah n +1, maka Anda dapat secara eksplisit memberi tahu LINQ ke SQL untuk memuat semua item terkait dalam sekali jalan (meskipun ini mungkin merupakan topik lanjutan saat Anda lebih terbiasa dengan L2S). Contoh di bawah ini mengatakan "ketika Anda memuat Posting, juga memuat semua catatan yang terkait dengannya melalui kunci asing yang diwakili oleh properti 'Post_metas'":

var dataLoadOptions = new DataLoadOptions();
dataLoadOptions.LoadWith<Post>(p => p.Post_metas);

var dataContext = new MyDataContext();
dataContext.LoadOptions = dataLoadOptions;

var post = Posts.Single(p => p.ID == 1); // Post_metas loaded automagically

Dimungkinkan untuk melakukan banyak LoadWithpanggilan pada satu set DataLoadOptionsuntuk tipe yang sama, atau banyak tipe berbeda. Jika Anda melakukan ini banyak, Anda mungkin hanya ingin mempertimbangkan caching.

Damian Powell
sumber
1
LinqPad dan CRM 2016 ?
Kiquenet
49

Daniel memiliki penjelasan yang baik tentang hubungan sintaksis, tetapi saya mengumpulkan dokumen ini untuk tim saya agar lebih mudah dimengerti oleh mereka. Semoga ini bisa membantu seseorangmasukkan deskripsi gambar di sini

Talspaugh27
sumber
Itu tidak akan berhasil ketika Anda hanya berurusan dengan daftar nilai seperti kita di sini. Tidak ada properti id pada objek.
Talspaugh27
Saya memang menemukan ini sangat berguna, tetapi saya mendapatkan kesalahan yang mengharuskan saya untuk menambahkan kolom bergabung. Juga melihat jawaban yang diposting oleh @ Mark Byers, kolom yang bergabung memiliki Post_IDbidang di alias kedua meta => meta.Post_ID. Dalam contoh di ilustrasi ini, g.idbagian dari pernyataan pilih asli JOIN gStatus g on g.idtidak direplikasi dalam ekspresi Lambda akhir.
SausageFingers
3
Saya tidak mencoba memposting ini sebagai referensi ke LINQ aktual yang diperlukan untuk menjawab diposting oleh OP, itu lebih merupakan referensi untuk bagaimana memindahkan SQL ke format Linq sehingga input saya sedikit berbeda dari pertanyaan awal. Jika saya telah membuat kelas untuk nilai gStatus saya akan meletakkan properti id di atasnya dan kemudian ya itu akan bergabung dengan g => g.id. Saya menggunakan daftar nilai untuk mencoba menjaga kode sesederhana mungkin.
Talspaugh27
@ Talspaugh27 Jadi mengapa dalam query SQL apakah itu bergabung ke gStatus di g.id? Apakah itu kesalahan atau disengaja?
Drammy
@ Drammy dalam tabel sql setiap kolom harus memiliki nama, jadi karena ini adalah tabel 1 kolom yang ketat untuk menahan id ini, saya hanya menggunakan kolom bernama id, Daftar <int> tidak memiliki masalah itu. Jika saya telah mengaturnya seperti itu public class IdHolder{ int id } maka menggunakan objek itu di gStatus List<IdHolder> gStatus = new List<IdHolder>(); gStatus.add(new IdHolder(){id = 7}); gStatus.add(new IdHolder(){id = 8}); maka itu akan mengubah Linq menjadi t =>t.value.TaskStatusId, g=>g.id apakah perubahan itu masuk akal?
Talspaugh27
37

Selektor kunci Anda salah. Mereka harus mengambil objek dari jenis tabel yang dimaksud dan mengembalikan kunci untuk digunakan dalam bergabung. Saya pikir maksud Anda ini:

var query = database.Posts.Join(database.Post_Metas,
                                post => post.ID,
                                meta => meta.Post_ID,
                                (post, meta) => new { Post = post, Meta = meta });

Anda dapat menerapkan klausa di mana sesudahnya, bukan sebagai bagian dari pemilih kunci.

Mark Byers
sumber
9

Posting karena ketika saya memulai LINQ + EntityFramework, saya menatap contoh-contoh ini selama sehari.

Jika Anda menggunakan EntityFramework, dan Anda memiliki properti navigasi yang disebutkan Metapada Postobjek model yang Anda atur, ini mudah. Jika Anda menggunakan entitas dan tidak memiliki properti navigasi itu, tunggu apa lagi?

database
  .Posts
  .Where(post => post.ID == id)
  .Select(post => new { post, post.Meta });

Jika Anda melakukan kode terlebih dahulu, Anda akan mengatur properti dengan demikian:

class Post {
  [Key]
  public int ID {get; set}
  public int MetaID { get; set; }
  public virtual Meta Meta {get; set;}
}
Andy V
sumber
5

Saya telah melakukan sesuatu seperti ini;

var certificationClass = _db.INDIVIDUALLICENSEs
    .Join(_db.INDLICENSECLAsses,
        IL => IL.LICENSE_CLASS,
        ILC => ILC.NAME,
        (IL, ILC) => new { INDIVIDUALLICENSE = IL, INDLICENSECLAsse = ILC })
    .Where(o => 
        o.INDIVIDUALLICENSE.GLOBALENTITYID == "ABC" &&
        o.INDIVIDUALLICENSE.LICENSE_TYPE == "ABC")
    .Select(t => new
        {
            value = t.PSP_INDLICENSECLAsse.ID,
            name = t.PSP_INDIVIDUALLICENSE.LICENSE_CLASS,                
        })
    .OrderBy(x => x.name);
Mahib
sumber
4

Bisa jadi sesuatu seperti

var myvar = from a in context.MyEntity
            join b in context.MyEntity2 on a.key equals b.key
            select new { prop1 = a.prop1, prop2= b.prop1};
pepitomb
sumber
1

1 sama dengan 1 dua tabel yang berbeda

var query = from post in database.Posts
            join meta in database.Post_Metas on 1 equals 1
            where post.ID == id
            select new { Post = post, Meta = meta };
mtngunay
sumber
1

Kueri LINQ ini seharusnya bekerja untuk Anda. Ini akan mendapatkan semua posting yang memiliki postingan meta.

var query = database.Posts.Join(database.Post_Metas,
                                post => post.postId, // Primary Key
                                meta => meat.postId, // Foreign Key
                                (post, meta) => new { Post = post, Meta = meta });

Query SQL Setara

Select * FROM Posts P
INNER JOIN Post_Metas pm ON pm.postId=p.postId
Ahamed Ishak
sumber
Anda menutup tanda kurung di mana setelah param ketiga ... "tidak ada kelebihan untuk Bergabung membutuhkan tiga argumen"
LastTribunal
3
Ini identik dengan jawaban yang diterima dan 7 tahun kemudian -1
reggaeguitar