เข้าร่วม / ที่ไหนด้วย LINQ และ Lambda


458

ฉันมีปัญหากับแบบสอบถามที่เขียนใน LINQ และ Lambda จนถึงตอนนี้ฉันได้รับข้อผิดพลาดมากมายที่นี่เป็นรหัสของฉัน:

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

ฉันใหม่กับการใช้ LINQ ดังนั้นฉันไม่แน่ใจว่าแบบสอบถามนี้ถูกต้องหรือไม่


11
คุณพยายามทำอะไร
GermánRodríguez

4
คุณต้องการให้แบบสอบถามทำอะไรในประโยค
นักล่า

6
ตัวเลือกที่สำคัญของคุณจะถูกวิธีที่ซับซ้อนเกินไป หากคุณต้องการเลือกตาม ID เพียงแค่ x => x.ID นั้นใช้ได้
Eric Lippert

1
ฉันต้องการรับโพสต์จากฐานข้อมูลและข้อมูลเมตาของโพสต์นั้น
David

คำตอบ:


1057

ฉันพบว่าหากคุณคุ้นเคยกับไวยากรณ์ SQL การใช้ไวยากรณ์คิวรี LINQ นั้นชัดเจนมากขึ้นเป็นธรรมชาติและทำให้ง่ายต่อการตรวจพบข้อผิดพลาด:

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

หากคุณติดกับการใช้แลมบ์ดาจริงๆแล้วไวยากรณ์ของคุณค่อนข้างจะปิดอยู่ นี่คือแบบสอบถามเดียวกันโดยใช้วิธีการขยาย 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

10
@Emanuele Greco เกี่ยวกับการแก้ไขของคุณ "ฟิลด์ Equality on ID ตั้งอยู่ในเงื่อนไข JOIN คุณไม่จำเป็นต้องใช้ WHERE clause!": WHERE clause ไม่ได้ทดสอบความเท่าเทียมกันระหว่างฟิลด์ ID มันเป็นการทดสอบความเท่าเทียมกันระหว่าง Post ID คอลัมน์และพารามิเตอร์ id ประกาศนอกแบบสอบถาม
Daniel Schaffer

9
ชิ้นส่วนที่ยอดเยี่ยม lambdaและเป็นคำพูดที่ใช้งานง่ายและเข้าใจ
Piotr Kula

1
ตัวอย่างที่ดีเลิศ
ของเล่น

1
บางครั้งคำอธิบายของแลมบ์ดาเขียนด้วยแลมบ์ดา อธิบายได้ดี
หยิก

80

คุณสามารถทำได้สองวิธีด้วยวิธีนี้ ใช้LINQPad (ล้ำค่าถ้าคุณใหม่กับ LINQ) และฐานข้อมูลหุ่นฉันสร้างแบบสอบถามต่อไปนี้:

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

หรือ

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

โดยเฉพาะอย่างยิ่งในกรณีนี้ฉันคิดว่าไวยากรณ์ LINQ นั้นสะอาดกว่า (ฉันเปลี่ยนระหว่างทั้งสองขึ้นอยู่กับว่าอ่านง่ายที่สุด)

สิ่งที่ฉันต้องการชี้ให้เห็นก็คือถ้าคุณมีคีย์ต่างประเทศที่เหมาะสมในฐานข้อมูลของคุณ (ระหว่างโพสต์และ post_meta) คุณอาจไม่จำเป็นต้องเข้าร่วมอย่างชัดเจนจนกว่าคุณจะพยายามโหลดบันทึกจำนวนมาก . ตัวอย่างของคุณดูเหมือนจะระบุว่าคุณพยายามโหลดโพสต์เดียวและเป็นข้อมูลเมตา สมมติว่ามีเร็กคอร์ด post_meta จำนวนมากสำหรับแต่ละโพสต์จากนั้นคุณสามารถทำสิ่งต่อไปนี้:

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

หากคุณต้องการหลีกเลี่ยงปัญหา n + 1 คุณสามารถบอก LINQ กับ SQL ให้โหลดรายการที่เกี่ยวข้องทั้งหมดได้ในคราวเดียว (แม้ว่านี่อาจเป็นหัวข้อขั้นสูงสำหรับเมื่อคุณคุ้นเคยกับ L2S มากขึ้น) ตัวอย่างด้านล่างบอกว่า "เมื่อคุณโหลดโพสต์โหลดระเบียนทั้งหมดที่เกี่ยวข้องกับมันผ่านคีย์ต่างประเทศที่แสดงโดยคุณสมบัติ '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

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


1
LinqPadและ CRM 2016 ?
Kiquenet

49

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


ที่ไม่ทำงานเมื่อคุณเพียงแค่จัดการกับรายการของค่าเช่นเราอยู่ที่นี่ ไม่มีคุณสมบัติ id บนวัตถุ
Talspaugh27

ฉันพบว่ามีประโยชน์จริง ๆ แต่ฉันพบข้อผิดพลาดที่ทำให้ฉันต้องเพิ่มคอลัมน์การเข้าร่วม นอกจากนี้ยังมองไปที่คำตอบโพสต์โดย @ Mark Byers คอลัมน์เข้าร่วมมีข้อมูลในนามแฝงที่สองPost_ID meta => meta.Post_IDในตัวอย่างในภาพประกอบนี้g.idส่วนของคำสั่ง select ต้นฉบับJOIN gStatus g on g.idจะไม่จำลองแบบในนิพจน์แลมบ์ดาสุดท้าย
SausageFingers

3
ฉันไม่ได้พยายามโพสต์สิ่งนี้เป็นการอ้างอิงถึง linq จริงที่ต้องใช้เพื่อตอบโพสต์โดย OP มันเป็นการอ้างอิงเพิ่มเติมสำหรับวิธีการย้าย SQL ไปเป็นรูปแบบ Linq ดังนั้นอินพุตของฉันจึงแตกต่างจากคำถามเดิมเล็กน้อย ถ้าฉันสร้างคลาสสำหรับค่า gStatus ฉันจะใส่ id คุณสมบัติไว้แล้วใช่มันจะได้เข้าร่วมกับ g => g.id ฉันใช้รายการค่าเพื่อพยายามทำให้รหัสง่ายที่สุดเท่าที่จะทำได้
Talspaugh27

@ Talspaugh27 แล้วทำไมแบบสอบถาม SQL ถึงได้เข้าร่วม gStatus บน g.id? นั่นเป็นความผิดพลาดหรือเจตนา?
รัมมอน

@Drammy ในตาราง sql แต่ละคอลัมน์จะต้องมีชื่อดังนั้นเนื่องจากนี่เป็นตาราง 1 คอลัมน์อย่างเคร่งครัดสำหรับการเก็บรหัสเหล่านี้ฉันเพิ่งใช้คอลัมน์ชื่อ id รายการ List <int> ไม่มีปัญหานั้น หากฉันตั้งค่าไว้เช่น public class IdHolder{ int id } นั้นใช้วัตถุนั้นใน gStatus List<IdHolder> gStatus = new List<IdHolder>(); gStatus.add(new IdHolder(){id = 7}); gStatus.add(new IdHolder(){id = 8}); แล้วมันจะเปลี่ยน Linq ให้เป็นการt =>t.value.TaskStatusId, g=>g.id เปลี่ยนแปลงที่สมเหตุสมผลหรือไม่?
Talspaugh27

37

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

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

คุณสามารถใช้ส่วนคำสั่งหลังจากนั้นไม่เป็นส่วนหนึ่งของตัวเลือกคีย์


9

โพสต์เพราะเมื่อฉันเริ่ม LINQ + EntityFramework ฉันจ้องที่ตัวอย่างเหล่านี้เป็นเวลาหนึ่งวัน

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

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

หากคุณกำลังทำโค้ดก่อนคุณต้องตั้งค่าคุณสมบัติดังนี้:

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

5

ฉันทำอะไรแบบนี้

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

4

มันอาจเป็นอะไรก็ได้

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

1

1 เท่ากับ 1 เข้าร่วมตารางที่แตกต่างกันสองสอง

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

1

แบบสอบถาม linq นี้ควรทำงานให้คุณ มันจะได้รับโพสต์ทั้งหมดที่มีเมตาโพสต์

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

แบบสอบถาม SQL ที่เทียบเท่ากัน

Select * FROM Posts P
INNER JOIN Post_Metas pm ON pm.postId=p.postId

คุณปิดวงเล็บที่หลังจากพารามิเตอร์ที่สาม ... "ไม่มีการโหลดเกินพิกัดสำหรับเข้าร่วมใช้เวลาสามข้อโต้แย้ง"
LastTribunal

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