Cara melakukan Gabung di antara beberapa tabel di LINQ lambda


92

Saya mencoba melakukan Gabung antara beberapa tabel di LINQ. Saya memiliki kelas-kelas berikut:

Product {Id, ProdName, ProdQty}

Category {Id, CatName}

ProductCategory{ProdId, CatId} //association table

Dan saya menggunakan kode berikut (di mana product, categorydan productcategoryadalah contoh dari kelas di atas):

var query = product.Join(productcategory, p => p.Id, pc => pc.ProdID, (p, pc) => new {product = p, productcategory = pc})
                   .Join(category, ppc => ppc.productcategory.CatId, c => c.Id, (ppc, c) => new { productproductcategory = ppc, category = c});

Dengan kode ini saya memperoleh objek dari kelas berikut:

QueryClass { productproductcategory, category}

Di mana kategori produkproduk berjenis:

ProductProductCategoryClass {product, productcategory}

Saya tidak mengerti di mana "tabel" yang tergabung, saya mengharapkan satu kelas yang berisi semua properti dari kelas yang terlibat.

Tujuan saya adalah mengisi objek lain dengan beberapa properti yang dihasilkan dari kueri:

CategorizedProducts catProducts = query.Select(m => new { m.ProdId = ???, m.CatId = ???, //other assignments });

bagaimana saya bisa mencapai tujuan ini?


Saya tidak mengerti ... mengapa m.ProdId = ??? bukannya prodId = m.ProdId ?
Adriano Repetti

Karena saya belum tahu sebelumnya bagaimana cara navigasi dan dapatkan ProdId
CiccioMiami

Jawaban:


182

Untuk gabungan, saya sangat menyukai sintaks kueri untuk semua detail yang dengan senang hati disembunyikan (tidak sedikit di antaranya adalah pengenal transparan yang terlibat dengan proyeksi perantara di sepanjang jalan yang terlihat dalam padanan sintaks titik). Namun, Anda bertanya tentang Lambdas yang menurut saya Anda memiliki semua yang Anda butuhkan - Anda hanya perlu menggabungkan semuanya.

var categorizedProducts = product
    .Join(productcategory, p => p.Id, pc => pc.ProdId, (p, pc) => new { p, pc })
    .Join(category, ppc => ppc.pc.CatId, c => c.Id, (ppc, c) => new { ppc, c })
    .Select(m => new { 
        ProdId = m.ppc.p.Id, // or m.ppc.pc.ProdId
        CatId = m.c.CatId
        // other assignments
    });

Jika perlu, Anda dapat menyimpan join ke variabel lokal dan menggunakannya kembali nanti, namun tidak memiliki detail lain yang sebaliknya, saya tidak melihat alasan untuk memperkenalkan variabel lokal.

Selain itu, Anda bisa melempar Selectke lambda terakhir dari yang kedua Join(sekali lagi, asalkan tidak ada operasi lain yang bergantung pada hasil gabungan) yang akan memberikan:

var categorizedProducts = product
    .Join(productcategory, p => p.Id, pc => pc.ProdId, (p, pc) => new { p, pc })
    .Join(category, ppc => ppc.pc.CatId, c => c.Id, (ppc, c) => new {
        ProdId = ppc.p.Id, // or ppc.pc.ProdId
        CatId = c.CatId
        // other assignments
    });

... dan melakukan upaya terakhir untuk menjual Anda pada sintaks kueri, ini akan terlihat seperti ini:

var categorizedProducts =
    from p in product
    join pc in productcategory on p.Id equals pc.ProdId
    join c in category on pc.CatId equals c.Id
    select new {
        ProdId = p.Id, // or pc.ProdId
        CatId = c.CatId
        // other assignments
    };

Tangan Anda mungkin terikat pada apakah kueri-sintaksis tersedia. Saya tahu beberapa toko memiliki mandat seperti itu - seringkali didasarkan pada anggapan bahwa sintaks-kueri agak lebih terbatas daripada sintaks titik. Ada alasan lain, seperti "mengapa saya harus mempelajari sintaks kedua jika saya dapat melakukan segalanya dan lebih banyak lagi dalam sintaks titik?" Seperti yang ditunjukkan bagian terakhir ini - ada detail yang disembunyikan sintaks-kueri yang dapat membuatnya layak untuk dirangkul dengan peningkatan pada keterbacaan yang dibawanya: semua proyeksi dan pengidentifikasi perantara yang harus Anda masak dengan senang hati tidak front-and-center- stage dalam versi query-syntax - mereka adalah background fluff. Dari kotak sabun saya sekarang - bagaimanapun, terima kasih atas pertanyaannya. :)


3
Terimakasih solusinya lebih lengkap. Saya setuju sintaks kueri dalam beberapa kasus lebih jelas tetapi tebakan Anda benar, saya telah diminta untuk menggunakan lambda. Selain itu saya harus membuat
penggabungan

@devgeezer Bagaimana jika kita membutuhkan kondisi penambahan dalam JOINpernyataan? Bagaimana kita melakukannya? Misalnya, di join pc in productcategory on p.Id equals pc.ProdIdbaris, kita perlu menambahkan and p.Id == 1.
Helikopter Serang Harambe

Tampaknya mencurigakan yang Anda inginkan p.Id == 1karena itu lebih merupakan filter di mana daripada itu adalah kriteria gabungan. Cara Anda akan melakukan bergabung pada lebih dari satu kriteria umumnya adalah dengan menggunakan jenis anonim: join pc in productcategory on new { Id = p.Id, Other = p.Other } equals new { Id = pc.ProdId, Other = pc.Other }. Ini bekerja di Linq-to-Objects, dan saya anggap hal yang sama akan bekerja dengan query database juga. Dengan Database, Anda mungkin dapat mengabaikan kueri penggabungan yang rumit dengan mendefinisikan kunci asing yang sesuai dan mengakses data terkait melalui properti terkait.
devgeezer

Terima kasih untuk solusi bersihnya.
Thomas.Benz

Dalam contoh Anda: dalam sintaks titik, ppc ppc.p adalah tipe anonim, bukan? Pada sintaks query, p.id yang Anda gunakan pada pemilihan terakhir masih berupa objek produk kan? Jadi dengan sintaks kueri lebih mudah jika Anda menggabungkan beberapa tabel untuk melakukan operasi di shema kembali terakhir seperti min minby?
CDrosos

12

Apa yang Anda lihat adalah apa yang Anda dapatkan - dan itulah yang Anda minta, di sini:

(ppc, c) => new { productproductcategory = ppc, category = c}

Itu adalah ekspresi lambda yang mengembalikan tipe anonim dengan dua properti tersebut.

Di CategorizedProducts, Anda hanya perlu menggunakan properti tersebut:

CategorizedProducts catProducts = query.Select(
      m => new { 
             ProdId = m.productproductcategory.product.Id, 
             CatId = m.category.CatId, 
             // other assignments 
           });

Terima kasih. Saya memahami diskusi tentang kelas anonim tetapi propertinya hanya berisi objek kelas yang memenuhi kueri? Dan apa yang terjadi setelah saya melakukan 2 penggabungan? productproductcategory.product tidak tergabung dengan kategori kan?
CiccioMiami

@CiccioMiami: Ya, properti adalah referensi ke objek, ya. Tidak terlalu jelas apa yang Anda maksud dengan "tidak bergabung" - informasi apa yang tidak Anda dapatkan dari kueri yang ingin Anda dapatkan?
Jon Skeet

Dengan bergabung pertama, saya mendapatkan gabungan antara produk dan kategori produk. Dengan yang kedua saya mendapatkan gabungan antara kategori produk (produk bergabung) dan kategori. Itu berarti informasi tentang beberapa gabungan hanya terdapat di kategori produk produk. Ini berarti produk (dan kategori) baru saja digabungkan dengan kategori produk.
CiccioMiami

1
@CiccioMiami: Maaf, saya tidak mengikuti Anda - tetapi jika Anda menentukan gabungan, itu akan berhasil. Sudahkah Anda mencoba menggunakan kode dalam jawaban saya? Apakah itu tidak melakukan apa yang Anda inginkan?
Jon Skeet

Maaf, saya ingin mendapatkan kode Anda. Penugasan CatId bekerja dengan baik. Untuk ProdIditu harus m.productproductcategory.product.IdOR m.productproductcategory.productcategory.ProdId. Kedua tugas tersebut berbeda, pertama pada produk (digabungkan dengan productcategory) yang kedua adalah dengan productcategorybergabung dengan keduanya productdan category. Apakah Anda mengikuti alasan saya?
CiccioMiami

5

lihat kode contoh ini dari proyek saya

public static IList<Letter> GetDepartmentLettersLinq(int departmentId)
{
    IEnumerable<Letter> allDepartmentLetters =
        from allLetter in LetterService.GetAllLetters()
        join allUser in UserService.GetAllUsers() on allLetter.EmployeeID equals allUser.ID into usersGroup
        from user in usersGroup.DefaultIfEmpty()// here is the tricky part
        join allDepartment in DepartmentService.GetAllDepartments() on user.DepartmentID equals allDepartment.ID
        where allDepartment.ID == departmentId
        select allLetter;

    return allDepartmentLetters.ToArray();
}

dalam kode ini saya bergabung dengan 3 tabel dan saya meludah kondisi join dari klausa mana

catatan: kelas Services hanya membengkokkan (merangkum) operasi database


2
 public ActionResult Index()
    {
        List<CustomerOrder_Result> obj = new List<CustomerOrder_Result>();

       var  orderlist = (from a in db.OrderMasters
                         join b in db.Customers on a.CustomerId equals b.Id
                         join c in db.CustomerAddresses on b.Id equals c.CustomerId
                         where a.Status == "Pending"
                         select new
                         {
                             Customername = b.Customername,
                             Phone = b.Phone,
                             OrderId = a.OrderId,
                             OrderDate = a.OrderDate,
                             NoOfItems = a.NoOfItems,
                             Order_amt = a.Order_amt,
                             dis_amt = a.Dis_amt,
                             net_amt = a.Net_amt,
                             status=a.Status,  
                             address = c.address,
                             City = c.City,
                             State = c.State,
                             Pin = c.Pin

                         }) ;
       foreach (var item in orderlist)
       {

           CustomerOrder_Result clr = new CustomerOrder_Result();
           clr.Customername=item.Customername;
           clr.Phone = item.Phone;
           clr.OrderId = item.OrderId;
           clr.OrderDate = item.OrderDate;
           clr.NoOfItems = item.NoOfItems;
           clr.Order_amt = item.Order_amt;
           clr.net_amt = item.net_amt;
           clr.address = item.address;
           clr.City = item.City;
           clr.State = item.State;
           clr.Pin = item.Pin;
           clr.status = item.status;

           obj.Add(clr);



       }

1
Meskipun cuplikan kode ini dapat menyelesaikan pertanyaan, menyertakan penjelasan sangat membantu meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa mendatang, dan orang-orang itu mungkin tidak tahu alasan saran kode Anda.
Dr Rob Lang

0
var query = from a in d.tbl_Usuarios
                    from b in d.tblComidaPreferidas
                    from c in d.tblLugarNacimientoes
                    select new
                    {
                        _nombre = a.Nombre,
                        _comida = b.ComidaPreferida,
                        _lNacimiento = c.Ciudad
                    };
        foreach (var i in query)
        {
            Console.WriteLine($"{i._nombre } le gusta {i._comida} y nació en {i._lNacimiento}");
        }

sederhana saja, tetapi lebih baik dengan lambda exp seperti yang dikatakan beberapa orang.
Alex Martinez

0

sudah lama tetapi jawaban saya dapat membantu seseorang:

jika Anda sudah mendefinisikan relasinya dengan benar, Anda dapat menggunakan ini:

        var res = query.Products.Select(m => new
        {
            productID = product.Id,
            categoryID = m.ProductCategory.Select(s => s.Category.ID).ToList(),
        }).ToList();
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.