Entity framework linq query รวม () เอนทิตีลูกหลายรายการ


176

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

คือผมมี 4 ตาราง: Company, Employee, Employee_CarและEmployee_Country

บริษัท มีความสัมพันธ์แบบ 1: m กับพนักงาน

พนักงานมีความสัมพันธ์แบบ 1: m กับ Employee_Car และ Employee_Country

ถ้าฉันต้องการเขียนแบบสอบถามที่ส่งคืนข้อมูลจากทั้ง 4 ตารางฉันกำลังเขียน:

Company company = context.Companies
                         .Include("Employee.Employee_Car")
                         .Include("Employee.Employee_Country")
                         .FirstOrDefault(c => c.Id == companyID);

จะต้องมีวิธีที่สง่างามมากขึ้น! นี่เป็นเวลานานและสร้าง SQL ที่น่ากลัว

ฉันใช้ EF4 กับ VS 2010

คำตอบ:


201

ใช้วิธีการขยาย แทนที่NameOfContextด้วยชื่อของบริบทวัตถุของคุณ

public static class Extensions{
   public static IQueryable<Company> CompleteCompanies(this NameOfContext context){
         return context.Companies
             .Include("Employee.Employee_Car")
             .Include("Employee.Employee_Country") ;
     }

     public static Company CompanyById(this NameOfContext context, int companyID){
         return context.Companies
             .Include("Employee.Employee_Car")
             .Include("Employee.Employee_Country")
             .FirstOrDefault(c => c.Id == companyID) ;
      }

}

จากนั้นรหัสของคุณจะกลายเป็น

     Company company = 
          context.CompleteCompanies().FirstOrDefault(c => c.Id == companyID);

     //or if you want even more
     Company company = 
          context.CompanyById(companyID);

แต่ฉันต้องการใช้มันเช่นนี้: //inside public static class Extensions public static IQueryable<Company> CompleteCompanies(this DbSet<Company> table){ return table .Include("Employee.Employee_Car") .Include("Employee.Employee_Country") ; } //code will be... Company company = context.Companies.CompleteCompanies().FirstOrDefault(c => c.Id == companyID); //same for next advanced method
ฮามิด

บูลส์ระวัง ส่วนขยายควรเป็นพอร์ตแรกของการเรียก ... ดี ... ขยายฟังก์ชันการทำงานที่กำหนดไว้ล่วงหน้า
ComeIn

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

2
ตั้งแต่การแนะนำของ nameof (class) มันเป็นไปได้ที่จะใช้วิธีการนี้อย่างปลอดภัย ในกรณีที่มีการเปลี่ยนแปลงชื่อเอนทิตีมันจะถูกเลือกระหว่างการคอมไพล์ ตัวอย่าง: context.Companies.Include (nameof (Employee)) ในกรณีที่ต้องการลงชื่อเพิ่มเติมต้องเชื่อมโยงกับ nameof (Employee) + "." + nameof (Employee_Car)
Karl

เทคนิควิธีการขยายใช้ไม่ได้กับคำสั่งที่คอมไพล์ (อย่างน้อยไม่ได้อยู่ใน EFCore) ยืนยันที่นี่: github.com/aspnet/EntityFrameworkCore/issues/7016
Dunge

156

EF 4.1 ถึง EF 6

มีการพิมพ์อย่างมาก.Includeซึ่งอนุญาตให้ระบุความลึกของการโหลดด้วยความกระตือรือร้นโดยระบุนิพจน์ที่เลือกไปยังความลึกที่เหมาะสม:

using System.Data.Entity; // NB!

var company = context.Companies
                     .Include(co => co.Employees.Select(emp => emp.Employee_Car))
                     .Include(co => co.Employees.Select(emp => emp.Employee_Country))
                     .FirstOrDefault(co => co.companyID == companyID);

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

EF Core

EF Core มีวิธีการขยายใหม่.ThenInclude()แม้ว่าไวยากรณ์จะแตกต่างกันเล็กน้อย :

var company = context.Companies
                     .Include(co => co.Employees)
                           .ThenInclude(emp => emp.Employee_Car)
                      ...

ตามเอกสารที่ฉันจะเก็บ 'เยื้อง' พิเศษในการ.ThenIncludeรักษาสติของคุณ

ข้อมูลล้าสมัย (อย่าทำสิ่งนี้):

การโหลดหลานหลายคนสามารถทำได้ในขั้นตอนเดียว แต่ต้องมีการย้อนกลับของกราฟที่ค่อนข้างอึดอัดก่อนที่จะลงไปที่โหนดถัดไป (หมายเหตุ: วิธีนี้ใช้ไม่ได้ผลAsNoTracking()- คุณจะได้รับข้อผิดพลาดรันไทม์):

var company = context.Companies
         .Include(co => 
             co.Employees
                .Select(emp => emp.Employee_Car
                    .Select(ec => ec.Employee)
                    .Select(emp2 => emp2.Employee_Country)))
         .FirstOrDefault(co => co.companyID == companyID);

ดังนั้นฉันจะอยู่กับตัวเลือกแรก (หนึ่งรุ่นรวมต่อความลึกเอนทิตีใบไม้)


4
ฉันสงสัยว่าจะทำอย่างไรกับพิมพ์อย่างยิ่งรวมงบ การฉายภาพเด็กด้วย Select เป็นคำตอบ!

1
equiv ของฉันของ "co.Employees.Select (... )" แสดงข้อผิดพลาดทางไวยากรณ์ใน "Select" โดยกล่าวว่า "'พนักงาน' ไม่มีคำจำกัดความสำหรับ 'Select' [หรือวิธีการขยาย]" ฉันรวม System.Data.Entity แล้ว ฉันต้องการรับคอลัมน์เดียวจากตารางที่เข้าร่วม
Chris Walsh

1
ฉันมีตารางหลักที่อ้างอิงตารางเด็กสองครั้ง ด้วยสตริงเก่ารวมถึงไวยากรณ์มันเป็นเรื่องยากที่จะโหลดความสัมพันธ์ที่ถูกต้องล่วงหน้า วิธีนี้เฉพาะเจาะจงมากขึ้น โปรดจำไว้ว่าให้รวมเนมสเปซ System.Data.Entity สำหรับการรวมอย่างยิ่ง
Karl

1
ด้วย. net core 2.1 ฉันต้องการเนมสเปซ Microsoft.EntityFrameworkCore แทน System.Data.Entity
denvercoder9

27

คุณอาจพบบทความนี้ที่น่าสนใจที่สามารถใช้ได้ที่codeplex.com

บทความนำเสนอวิธีการใหม่ในการแสดงเคียวรีที่ขยายหลายตารางในรูปแบบของรูปร่างกราฟที่ประกาศ

นอกจากนี้บทความนี้ยังมีการเปรียบเทียบประสิทธิภาพโดยละเอียดของวิธีการใหม่นี้กับข้อความค้นหาของ EF การวิเคราะห์นี้แสดงให้เห็นว่า GBQ มีประสิทธิภาพสูงกว่าแบบสอบถามของ EF อย่างรวดเร็ว


วิธีนี้สามารถนำมาใช้ในแอปพลิเคชันที่ใช้งานจริงได้อย่างไร
Victor.Uduak

4

คุณสร้างคำสั่ง LINQ ไปยังเอนทิตีได้อย่างไรเพื่อโหลดวัตถุลูกโดยตรงแทนที่จะเรียกคุณสมบัติการอ้างอิงหรือโหลด ()

ไม่มีวิธีอื่น - ยกเว้นการนำการโหลดแบบสันหลังยาว

หรือโหลดด้วยตนเอง ....

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