วิธีดำเนินการเข้าร่วมระหว่างหลายตารางใน LINQ lambda


92

ฉันพยายามทำการเข้าร่วมระหว่างหลาย ๆ ตารางใน LINQ ฉันมีชั้นเรียนต่อไปนี้:

Product {Id, ProdName, ProdQty}

Category {Id, CatName}

ProductCategory{ProdId, CatId} //association table

และฉันจะใช้รหัสต่อไปนี้ (ที่product, categoryและproductcategoryกรณีของการเรียนข้างต้น):

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

ด้วยรหัสนี้ฉันได้รับวัตถุจากคลาสต่อไปนี้:

QueryClass { productproductcategory, category}

โดยที่ประเภทผลิตภัณฑ์ผลิตภัณฑ์เป็นประเภท:

ProductProductCategoryClass {product, productcategory}

ฉันไม่เข้าใจว่า "ตาราง" ที่เข้าร่วมอยู่ที่ไหนฉันคาดหวังว่าจะมีคลาสเดียวที่มีคุณสมบัติทั้งหมดจากคลาสที่เกี่ยวข้อง

เป้าหมายของฉันคือเติมข้อมูลวัตถุอื่นด้วยคุณสมบัติบางอย่างที่เป็นผลมาจากการสืบค้น

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

ฉันจะบรรลุเป้าหมายนี้ได้อย่างไร


ฉันไม่เข้าใจ ... ทำไมm.ProdId = ??? แทนprodId = m.ProdId ?
Adriano Repetti

เพราะไม่รู้ล่วงหน้าว่าจะนำทางอย่างไรและรับ ProdId
CiccioMiami

คำตอบ:


185

สำหรับการรวมฉันชอบไวยากรณ์การสืบค้นสำหรับรายละเอียดทั้งหมดที่ถูกซ่อนไว้อย่างมีความสุข (ไม่ใช่อย่างน้อยที่สุดคือตัวระบุโปร่งใสที่เกี่ยวข้องกับการคาดการณ์ระดับกลางตลอดทางที่ปรากฏในการเทียบเท่าไวยากรณ์ดอท) อย่างไรก็ตามคุณถามเกี่ยวกับ Lambdas ซึ่งฉันคิดว่าคุณมีทุกสิ่งที่คุณต้องการ - คุณแค่ต้องรวบรวมทั้งหมดเข้าด้วยกัน

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

หากคุณต้องการคุณสามารถบันทึกการเข้าร่วมลงในตัวแปรท้องถิ่นและนำกลับมาใช้ใหม่ได้ในภายหลังอย่างไรก็ตามไม่มีรายละเอียดอื่น ๆ ในทางตรงกันข้ามฉันไม่เห็นเหตุผลที่จะแนะนำตัวแปรท้องถิ่น

นอกจากนี้คุณสามารถโยนSelectเข้าไปในแลมด้าสุดท้ายของวินาทีJoin(อีกครั้งหากไม่มีการดำเนินการอื่นใดที่ขึ้นอยู่กับผลการรวม) ซึ่งจะให้:

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

... และพยายามครั้งสุดท้ายที่จะขายคุณเกี่ยวกับไวยากรณ์การสืบค้นสิ่งนี้จะมีลักษณะดังนี้:

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

มือของคุณอาจผูกติดอยู่กับว่ามีคิวรี - ไวยากรณ์หรือไม่ ฉันรู้ว่าร้านค้าบางแห่งมีเอกสารดังกล่าว - มักจะขึ้นอยู่กับความคิดที่ว่าคิวรี - วากยสัมพันธ์ค่อนข้าง จำกัด มากกว่าไวยากรณ์ดอท มีเหตุผลอื่น ๆ เช่น "ทำไมฉันจึงควรเรียนรู้ไวยากรณ์ที่สองถ้าฉันสามารถทำทุกอย่างและอื่น ๆ ในดอทไวยากรณ์ ดังที่แสดงในส่วนสุดท้ายนี้ - มีรายละเอียดที่ไวยากรณ์ของคิวรีซ่อนไว้ซึ่งสามารถทำให้คุ้มค่ากับการปรับปรุงเพื่อให้อ่านง่ายขึ้น: การคาดการณ์และตัวระบุระดับกลางทั้งหมดที่คุณต้องปรุงขึ้นนั้นไม่ได้อยู่ตรงหน้าและตรงกลางอย่างมีความสุข ขั้นตอนในเวอร์ชันคิวรี - ไวยากรณ์ - เป็นพื้นหลังปุย ปิดกล่องสบู่ของฉันตอนนี้ - อย่างไรก็ตามขอบคุณสำหรับคำถาม :)


3
ขอบคุณโซลูชันของคุณสมบูรณ์ยิ่งขึ้น ฉันยอมรับว่าไวยากรณ์การสืบค้นในบางกรณีมีความชัดเจนมากกว่า แต่คุณเดาถูกฉันถูกขอให้ใช้แลมด้า ยิ่งไปกว่านั้นฉันต้องทำให้สิ่งนี้รวมกันมากกว่า 6 ตารางและสัญกรณ์จุดในกรณีนี้จะเรียบร้อยกว่า
CiccioMiami

@devgeezer จะเป็นอย่างไรหากเราต้องการเงื่อนไขเพิ่มเติมในJOINคำสั่ง เราจะทำอย่างนั้นได้อย่างไร? ยกตัวอย่างเช่นในสายเราต้องเพิ่มjoin pc in productcategory on p.Id equals pc.ProdId and p.Id == 1
เฮลิคอปเตอร์โจมตีฮารัมเบ

ดูเหมือนสงสัยว่าคุณต้องการp.Id == 1เนื่องจากเป็นส่วนที่กรองมากกว่าเกณฑ์การเข้าร่วม join pc in productcategory on new { Id = p.Id, Other = p.Other } equals new { Id = pc.ProdId, Other = pc.Other }ในแบบที่คุณต้องการทำร่วมมากกว่าหนึ่งเกณฑ์ทั่วไปคือการใช้ที่ไม่ระบุชื่อประเภท: สิ่งนี้ใช้ได้ใน Linq-to-Objects และฉันคิดว่าสิ่งนี้จะใช้ได้กับการสืบค้นฐานข้อมูลเช่นกัน ด้วยฐานข้อมูลคุณอาจสามารถยกเลิกการค้นหาการรวมที่ซับซ้อนได้โดยกำหนดคีย์ต่างประเทศตามความเหมาะสมและเข้าถึงข้อมูลที่เกี่ยวข้องผ่านคุณสมบัติที่เกี่ยวข้อง
devgeezer

ขอบคุณสำหรับน้ำยาสะอาด
Thomas.Benz

ในตัวอย่างของคุณ: ในไวยากรณ์ dot ppc ppc.p เป็นประเภทที่ไม่ระบุตัวตนใช่ไหม ในไวยากรณ์ของแบบสอบถาม p.id ที่คุณใช้ในการเลือกครั้งสุดท้ายยังคงเป็นวัตถุผลิตภัณฑ์ใช่หรือไม่? ดังนั้นด้วยไวยากรณ์ของแบบสอบถามจะง่ายกว่าถ้าคุณเข้าร่วมหลายตารางเพื่อดำเนินการใน Shema ที่ส่งคืนสุดท้ายเช่น min minby?
CDrosos

12

สิ่งที่คุณเห็นคือสิ่งที่คุณได้รับ - และเป็นสิ่งที่คุณขอที่นี่:

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

นั่นคือนิพจน์แลมบ์ดาที่ส่งคืนประเภทที่ไม่ระบุตัวตนพร้อมกับคุณสมบัติทั้งสองนั้น

ใน CategoryorizedProducts ของคุณคุณเพียงแค่ต้องผ่านคุณสมบัติเหล่านั้น:

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

ขอบคุณ. ฉันเข้าใจการสนทนาเกี่ยวกับคลาสที่ไม่ระบุชื่อ แต่คุณสมบัติของมันมีเพียงคลาสออบเจ็กต์ที่ตอบสนองการสืบค้น แล้วจะเกิดอะไรขึ้นหลังจากที่ฉันทำการ join 2 ครั้ง? productproductcategory.product ไม่เข้าร่วมกับหมวดหมู่ใช่ไหม
CiccioMiami

@CiccioMiami: คุณสมบัติคือการอ้างอิงถึงวัตถุใช่ ยังไม่ชัดเจนนักว่า "ไม่เข้าร่วม" หมายถึงอะไร - ข้อมูลใดที่คุณไม่ได้รับจากข้อความค้นหาที่คุณต้องการได้รับ
Jon Skeet

ในการเข้าร่วมครั้งแรกฉันได้รับการเข้าร่วมระหว่างผลิตภัณฑ์และหมวดหมู่ผลิตภัณฑ์ ประการที่สองฉันได้รับการเข้าร่วมระหว่างหมวดหมู่ผลิตภัณฑ์ (ผลิตภัณฑ์ที่เข้าร่วม) และหมวดหมู่ นั่นหมายความว่าข้อมูลเกี่ยวกับการรวมหลายรายการจะมีอยู่ในหมวดหมู่ผลิตภัณฑ์เท่านั้น ซึ่งหมายความว่าผลิตภัณฑ์ (และหมวดหมู่) จะเข้าร่วมกับหมวดหมู่ผลิตภัณฑ์เท่านั้น
CiccioMiami

1
@CiccioMiami: ขออภัยฉันไม่ได้ติดตามคุณ - แต่ถ้าคุณระบุการเข้าร่วมก็จะทำเช่นนั้น คุณได้ลองใช้รหัสในคำตอบของฉันแล้วหรือยัง? มันไม่ทำตามที่คุณต้องการ?
Jon Skeet

ขออภัยฉันต้องการเข้าถึงรหัสของคุณ มอบหมายCatId งานได้ดี สำหรับProdIdมันควรจะเป็นหรือm.productproductcategory.product.Id m.productproductcategory.productcategory.ProdIdทั้งสองได้รับมอบหมายแตกต่างกันเป็นครั้งแรกเกี่ยวกับผลิตภัณฑ์ (ร่วมกับproductcategory) สองคือมีproductcategoryร่วมกับทั้งสองและproduct categoryคุณทำตามเหตุผลของฉันหรือไม่?
CiccioMiami

5

ลองดูโค้ดตัวอย่างนี้จากโครงการของฉัน

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

ในรหัสนี้ฉันเข้าร่วม 3 ตารางและฉันคายเงื่อนไขการเข้าร่วมจากที่ข้อ

หมายเหตุ: คลาสบริการจะบิดเบี้ยว (ห่อหุ้ม) การทำงานของฐานข้อมูล


3
 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
แม้ว่าข้อมูลโค้ดนี้จะช่วยแก้ปัญหาได้ แต่การมีคำอธิบายจะช่วยปรับปรุงคุณภาพของโพสต์ของคุณได้มาก โปรดจำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตและบุคคลเหล่านั้นอาจไม่ทราบสาเหตุของการแนะนำโค้ดของคุณ
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}");
        }

ง่ายๆแค่นั้น แต่จะดีกว่ากับ lambda exp เหมือนที่บางคนพูด
Alex Martinez

0

เป็นเวลานานแล้ว แต่คำตอบของฉันอาจช่วยใครบางคนได้:

หากคุณกำหนดความสัมพันธ์อย่างถูกต้องแล้วคุณสามารถใช้สิ่งนี้:

        var res = query.Products.Select(m => new
        {
            productID = product.Id,
            categoryID = m.ProductCategory.Select(s => s.Category.ID).ToList(),
        }).ToList();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.