LINQ lambda'da birden çok tablo arasında birleştirme işlemi nasıl gerçekleştirilir


92

LINQ'da birden çok tablo arasında bir Birleştirme gerçekleştirmeye çalışıyorum . Aşağıdaki sınıflarım var:

Product {Id, ProdName, ProdQty}

Category {Id, CatName}

ProductCategory{ProdId, CatId} //association table

Ve aşağıdaki koduyla (burada product, categoryve productcategoryyukarıda belirtilen sınıfların örnekleri olan):

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

Bu kodla aşağıdaki sınıftan bir nesne elde ediyorum:

QueryClass { productproductcategory, category}

Ürün kategorisinin türü olduğunda:

ProductProductCategoryClass {product, productcategory}

Birleştirilen "tablonun" nerede olduğunu anlamıyorum , ilgili sınıfların tüm özelliklerini içeren tek bir sınıf bekliyordum .

Amacım, sorgudan kaynaklanan bazı özelliklerle başka bir nesneyi doldurmak:

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

bu hedefe nasıl ulaşabilirim?


Anlamadım ... neden m.ProdId = ??? prodId = m.ProdId yerine ?
Adriano Repetti

Çünkü nasıl gezineceğimi ve ProdId'i nasıl alacağımı önceden bilmediğim için
CiccioMiami

Yanıtlar:


182

Birleştirmeler için, mutlu bir şekilde gizlenen tüm ayrıntılar için sorgu sözdizimini şiddetle tercih ederim (bunların en önemlisi, nokta sözdizimi eşdeğerinde görünen yol boyunca ara projeksiyonlarla ilgili şeffaf tanımlayıcılardır). Ancak, ihtiyacınız olan her şeye sahip olduğunuzu düşündüğüm Lambdas'ı sordunuz - sadece hepsini bir araya getirmeniz gerekiyor.

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

Gerekirse, birleşimi yerel bir değişkene kaydedebilir ve daha sonra yeniden kullanabilirsiniz, ancak aksine diğer ayrıntılar eksikse, yerel değişkeni tanıtmak için bir neden göremiyorum.

Ayrıca, Selectsaniyenin son lambdasına da atabilirsiniz Join(birleştirme sonuçlarına bağlı başka hiçbir işlem olmaması koşuluyla):

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

... ve sizi sorgu sözdizimine göre satmak için son bir girişimde bulunursanız, bu şöyle görünecektir:

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

Elleriniz sorgu sözdiziminin mevcut olup olmadığına bağlı olabilir. Bazı dükkanların bu tür yetkileri olduğunu biliyorum - genellikle sorgu sözdiziminin nokta sözdiziminden biraz daha sınırlı olduğu fikrine dayanarak. "Nokta sözdiziminde her şeyi ve daha fazlasını yapabilirsem neden ikinci bir sözdizimi öğrenmeliyim?" Gibi başka nedenler de var. Bu son bölümün gösterdiği gibi - sorgu-sözdiziminin sağladığı, okunabilirlikteki iyileştirme ile onu iyi bir şekilde benimsemeye değer kılan ayrıntılar var: pişirmeniz gereken tüm bu ara tahminler ve tanımlayıcılar ne mutlu ki önden ve merkezden değil. Sorgu-sözdizimi sürümünde sahne - bunlar arka plandadır. Şimdi sabun kutumdan - her neyse, soru için teşekkürler. :)


3
Teşekkürler, çözümünüz daha eksiksiz. Bazı durumlarda sorgu sözdiziminin daha açık olduğuna katılıyorum ama doğru tahmin ettiniz, benden lambda kullanmam istendi. Üstelik bu 6 yılı tabloyu birleştiren yapmak zorunda ve bu durumda nokta notasyonu daha düzgün
CiccioMiami

@devgeezer Ya ifadede ek koşula ihtiyacımız olursa JOIN? Bunu nasıl yaparız? Örneğin join pc in productcategory on p.Id equals pc.ProdIdsatıra eklememiz gerekiyor and p.Id == 1.
Harambe Attack Helicopter

Görünüşe p.Id == 1göre, bu bir birleştirme ölçütü olmaktan çok bir nerede filtresinden ibaret olduğu için istediğiniz gibi görünüyor . Bir birden fazla kritere katılmak yapacağın yolu genellikle bir anonim tür kullanmaktır: join pc in productcategory on new { Id = p.Id, Other = p.Other } equals new { Id = pc.ProdId, Other = pc.Other }. Bu, Linq-to-Objects'te çalışıyor ve ben de aynısının veritabanı sorguları için çalışacağını düşünüyorum. Veritabanları ile, yabancı anahtarları uygun şekilde tanımlayarak ve ilgili mülk aracılığıyla ilgili verilere erişerek karmaşık birleştirme sorgularından vazgeçebilirsiniz.
devgeezer

Temiz çözüm için teşekkürler.
Thomas.Benz

Örneğinizde: nokta sözdiziminde, ppc ppc.p anonim türlerdir değil mi? Sorgu sözdiziminde, son seçimde kullandığınız p.id hala bir ürün nesnesidir, değil mi? Öyleyse sorgu sözdizimi ile, birden çok tabloyu son dönen şema içinde min minby gibi işlemler yapmak için birleştirirseniz daha kolay olur mu?
CDrosos

12

Gördüğünüz şey elde ettiğiniz şeydir - ve burada tam olarak istediğiniz şey:

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

Bu, bu iki özelliğe sahip anonim bir tür döndüren bir lambda ifadesidir.

CategorizedProducts ürününüzde, yalnızca bu özellikler üzerinden gitmeniz gerekir:

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

Teşekkürler. Anonim sınıf hakkındaki tartışmayı anlıyorum, ancak özellikleri yalnızca sorguyu yerine getiren sınıf nesnelerini mi içeriyor? Ve 2 birleştirme yaptıktan sonra ne olur? productproductcategory.product kategoriye dahil değil değil mi?
CiccioMiami

@CiccioMiami: Evet, özellikler nesnelere referanslar , evet. "Katılmadım" ile neyi kastettiğin gerçekten net değil - sorgunuzdan almak istediğiniz hangi bilgileri almıyorsunuz?
Jon Skeet

İlk birleştirme ile ürünler ve ürün kategorisi arasındaki birleşimi elde ederim. İkincisi ile ürün kategorisi (birleştirilmiş ürün) ve kategori arasındaki birleşimi elde ediyorum. Bu, çoklu birleştirme hakkındaki bilgilerin sadece ürün kategorisinde bulunduğu anlamına gelir. Bu, ürünün (ve kategorinin) ürün kategorisi ile henüz birleştirildiği anlamına gelir.
CiccioMiami

1
@CiccioMiami: Üzgünüm, sizi takip etmiyorum - ancak katılmayı belirtirseniz, yapacaktır. Cevabımdaki kodu kullanmayı denediniz mi? İstediğini yapmıyor mu?
Jon Skeet

Üzgünüm, kodunuza ulaşmak istedim. CatId İşlerin atanması iyi. İçin ProdIdolması gerektiği m.productproductcategory.product.IdVEYA m.productproductcategory.productcategory.ProdId. İki atama farklıdır, birincisi ürün üzerindedir (ile birleştirilir productcategory), ikincisi productcategoryhem productve ile birleştirilir category. Benim mantığımı takip ediyor musun?
CiccioMiami

5

projemdeki bu örnek koda bak

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

bu kodda 3 tabloya katıldım ve birleştirme koşulunu where cümlesinden kullandım

not: Hizmetler sınıfları, veritabanı işlemlerini yalnızca çarpıktır (kapsüllenir)


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
Bu kod parçacığı soruyu çözebilirken, bir açıklama eklemek, yayınınızın kalitesini artırmaya gerçekten yardımcı olur. Gelecekte okuyucular için soruyu yanıtlayacağınızı ve bu kişilerin kod önerinizin nedenlerini bilmeyebileceklerini unutmayın.
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}");
        }

sadece bu kadar basit, ancak bazılarının dediği gibi lambda exp ile daha iyi.
Alex Martinez

0

uzun zaman oldu ama cevabım birine yardımcı olabilir:

ilişkiyi zaten doğru bir şekilde tanımladıysanız, bunu kullanabilirsiniz:

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