Linq to Entities เข้าร่วม vs groupjoin


182

ฉันค้นเว็บแล้ว แต่ฉันยังหาคำตอบง่าย ๆ ไม่ได้ มีคนช่วยอธิบายด้วยภาษาอังกฤษง่ายๆGroupJoinได้ไหม? มันแตกต่างจากภายในทั่วไปJoinอย่างไร ใช้บ่อยไหม มันเป็นเพียงไวยากรณ์ของวิธีการ? สิ่งที่เกี่ยวกับไวยากรณ์แบบสอบถาม ตัวอย่างรหัส c # น่าจะดี


ตาม MSDN การเข้าร่วมกลุ่มเป็นประโยคการเข้าร่วมที่มีนิพจน์เป็น เข้าร่วม clauseมีข้อมูลเพิ่มเติมและตัวอย่างรหัส มันคือการเข้าร่วมวงใน (ถ้าไม่มีองค์ประกอบใดในด้านขวาที่ตรงกันทางซ้ายคุณจะได้ผลลัพธ์เป็นโมฆะ); อย่างไรก็ตามผลลัพธ์จะถูกจัดเป็นกลุ่ม
Tim

คำตอบ:


375

พฤติกรรม

สมมติว่าคุณมีสองรายการ:

Id  Value
1   A
2   B
3   C

Id  ChildValue
1   a1
1   a2
1   a3
2   b1
2   b2

เมื่อคุณJoinทั้งสองรายการในIdสนามผลลัพธ์จะเป็น:

Value ChildValue
A     a1
A     a2
A     a3
B     b1
B     b2

เมื่อคุณGroupJoinทั้งสองรายการในIdสนามผลลัพธ์จะเป็น:

Value  ChildValues
A      [a1, a2, a3]
B      [b1, b2]
C      []

ดังนั้นJoinสร้างผลลัพธ์แบบแบน (ตาราง) ของค่าหลักและค่ารอง
GroupJoinสร้างรายการของรายการในรายการแรกแต่ละรายการมีกลุ่มของรายการที่เข้าร่วมในรายการที่สอง

นั่นเป็นเหตุผลที่Joinเทียบเท่าของINNER JOINใน SQL: Cมีรายการไม่ while GroupJoinมีค่าเท่ากับOUTER JOIN: Cอยู่ในชุดผลลัพธ์ แต่มีรายการว่างของรายการที่เกี่ยวข้อง (ในชุดผลลัพธ์ SQL จะมีแถวC - null)

วากยสัมพันธ์

ดังนั้นให้ทั้งสองรายการเป็นIEnumerable<Parent>และIEnumerable<Child>ตามลำดับ (ในกรณีของการ Linq เพื่อองค์กร: IQueryable<T>)

Join ไวยากรณ์จะเป็น

from p in Parent
join c in Child on p.Id equals c.Id
select new { p.Value, c.ChildValue }

ส่งคืนIEnumerable<X>ที่ X เป็นประเภทที่ไม่ระบุชื่อที่มีสองคุณสมบัติและValue ChildValueไวยากรณ์เคียวรีนี้ใช้Joinเมธอดภายใต้ประทุน

GroupJoin ไวยากรณ์จะเป็น

from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }

กลับโดยIEnumerable<Y>ที่ Y เป็นประเภทที่ไม่ระบุตัวตนประกอบด้วยคุณสมบัติประเภทหนึ่งParentและคุณสมบัติประเภทIEnumerable<Child>หนึ่ง ไวยากรณ์เคียวรีนี้ใช้GroupJoinเมธอดภายใต้ประทุน

เราสามารถทำได้select gในการสืบค้นหลังซึ่งจะเลือกรายการIEnumerable<IEnumerable<Child>>พูดรายการ ในหลายกรณีการเลือกที่มีพาเรนต์รวมอยู่จะมีประโยชน์มากกว่า

บางกรณีใช้

1. ผลิตการรวมภายนอกแบบเรียบ

ดังที่กล่าวไว้คำสั่ง ...

from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }

... สร้างรายชื่อผู้ปกครองที่มีกลุ่มลูก สิ่งนี้สามารถเปลี่ยนเป็นรายการแบบแบนของคู่พาเรนต์ - ชายโดยการเพิ่มขนาดเล็กสองรายการ:

from p in parents
join c in children on p.Id equals c.Id into g // <= into
from c in g.DefaultIfEmpty()               // <= flattens the groups
select new { Parent = p.Value, Child = c?.ChildValue }

ผลลัพธ์จะคล้ายกับ

Value Child
A     a1
A     a2
A     a3
B     b1
B     b2
C     (null)

โปรดทราบว่าตัวแปรช่วง cจะถูกใช้ซ้ำในคำสั่งด้านบน การทำเช่นนี้joinคำสั่งใด ๆสามารถถูกแปลงเป็น a ได้outer joinโดยการเพิ่มคำสั่งที่เทียบเท่าinto g from c in g.DefaultIfEmpty()กับjoinคำสั่งที่มีอยู่

นี่คือที่การสืบค้น (หรือครอบคลุม) ไวยากรณ์ส่อง ไวยากรณ์ของเมธอด (หรือคล่องแคล่ว) แสดงให้เห็นว่าเกิดอะไรขึ้นจริง แต่ยากที่จะเขียน:

parents.GroupJoin(children, p => p.Id, c => c.Id, (p, c) => new { p, c })
       .SelectMany(x => x.c.DefaultIfEmpty(), (x,c) => new { x.p.Value, c?.ChildValue } )

ดังนั้นแบนouter joinใน LINQ เป็นแบนโดยGroupJoinSelectMany

2. เก็บรักษาคำสั่งซื้อ

สมมติว่ารายชื่อผู้ปกครองนั้นมีความยาวขึ้นอีกเล็กน้อย UI บางรายการสร้างรายการของพาเรนต์ที่เลือกเป็นIdค่าในลำดับคงที่ มาใช้กัน:

var ids = new[] { 3,7,2,4 };

ตอนนี้ผู้ปกครองที่เลือกจะต้องถูกกรองออกจากรายการผู้ปกครองในลำดับที่แน่นอนนี้

ถ้าเราทำ ...

var result = parents.Where(p => ids.Contains(p.Id));

... ลำดับของparentsจะเป็นตัวกำหนดผลลัพธ์ หากผู้ปกครองได้รับคำสั่งIdผลที่ได้คือผู้ปกครอง 2, 3, 4, 7 ไม่ดี อย่างไรก็ตามเรายังสามารถใช้joinในการกรองรายการ และโดยใช้idsเป็นรายการแรกคำสั่งซื้อจะถูกเก็บไว้:

from id in ids
join p in parents on id equals p.Id
select p

ผลลัพธ์คือผู้ปกครอง 3, 7, 2, 4


ดังนั้นใน GroupJoin ค่าลูกจะมีวัตถุที่มีค่าที่เกี่ยวข้อง?
duyn9uyen

ดังที่คุณกล่าวว่า GroupJoin เป็นเหมือนการเข้าร่วมด้านนอก แต่ไวยากรณ์นั้น (linq ล้วนๆสำหรับการเข้าร่วมกลุ่ม) บอกว่ามันไม่เหมือนการเข้าร่วมด้านนอก แต่เข้าร่วมด้านนอกด้านซ้าย
Imad

2
ฉันคิดว่าฉันจะระบุว่า "Flat Outer Join" เป็น Left Outer Join
NetMage

1
อธิบายได้อย่างสมบูรณ์แบบตอนนี้ฉันเข้าใจแล้ว
peterincumbria

19

ตามeduLINQ :

วิธีที่ดีที่สุดในการเข้าถึงสิ่งที่ GroupJoin ทำคือคิดถึงการเข้าร่วม ที่นั่นแนวคิดโดยรวมคือเรามองผ่านลำดับการป้อนข้อมูล "ด้านนอก" พบรายการที่ตรงกันทั้งหมดจากลำดับ "ภายใน" (ขึ้นอยู่กับการคาดการณ์ที่สำคัญในแต่ละลำดับ) จากนั้นให้คู่ขององค์ประกอบการจับคู่ GroupJoin คล้ายยกเว้นว่าแทนที่จะยอมคู่ขององค์ประกอบก็มีผลเป็นผลเดี่ยวสำหรับแต่ละรายการ "นอก" ขึ้นอยู่กับสินค้าที่และลำดับของการจับคู่รายการ

ความแตกต่างเพียงอย่างเดียวคือข้อความสั่งคืน:

เข้าร่วม :

var lookup = inner.ToLookup(innerKeySelector, comparer); 
foreach (var outerElement in outer) 
{ 
    var key = outerKeySelector(outerElement); 
    foreach (var innerElement in lookup[key]) 
    { 
        yield return resultSelector(outerElement, innerElement); 
    } 
} 

กลุ่มงาน :

var lookup = inner.ToLookup(innerKeySelector, comparer); 
foreach (var outerElement in outer) 
{ 
    var key = outerKeySelector(outerElement); 
    yield return resultSelector(outerElement, lookup[key]); 
} 

อ่านเพิ่มเติมได้ที่นี่:

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.