Entity Framework - รวมคุณสมบัติหลายระดับ


376

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

ตามที่เป็นอยู่ตอนนี้วิธีการนี้:

public IEnumerable<ApplicationServer> GetAll()
{
    return this.Database.ApplicationServers
        .Include(x => x.ApplicationsWithOverrideGroup)                
        .Include(x => x.ApplicationWithGroupToForceInstallList)
        .Include(x => x.CustomVariableGroups)                
        .ToList();
}

จะเติมข้อมูลเฉพาะคุณสมบัติที่เปิดใช้งาน (ด้านล่าง) และไม่ใช่คุณสมบัติของแอปพลิเคชันหรือ CustomVariableGroup (ด้านล่าง) ฉันจะทำให้สิ่งนี้เกิดขึ้นได้อย่างไร

public class ApplicationWithOverrideVariableGroup : EntityBase
{
    public bool Enabled { get; set; }
    public Application Application { get; set; }
    public CustomVariableGroup CustomVariableGroup { get; set; }
}

สวัสดีทำไมฉันจะได้รับข้อยกเว้นเมื่อผมลองนี้ในการรวมชุดแล้วชุดหนึ่งระดับลง:Expression must be a member expression query.Include(e => e.Level1Collection.Select(l1 => l1.Level2Collection))
Joe.wang

1
@ BobHorn ฉันมีปัญหาเดียวกัน .. ในกรณีของฉันการซ้อนกันจะลึกลงไปหลายเลเยอร์ ใน SQL ที่สร้างขึ้นฉันสามารถเห็นคอลัมน์ทั้งหมดกลับมาด้วยชื่อนามแฝงที่แตกต่างกันเป็น c1, c2 บางอย่างเช่นนั้น คำถามของฉันคือฉันจะสร้างคอลเลกชัน DTO ที่ซ้อนกันได้จากทุกอย่างของฉันได้อย่างไร :( .. อาจเป็นไปได้ที่คุณจะสามารถใช้ตัวอย่างข้างต้นได้เองโดยที่เราจะส่งคืนคอลัมน์ทั้งหมดโดยไม่มี DTO ที่กำหนดเอง )
TechQuery

คำตอบ:


703

สำหรับ EF 6

using System.Data.Entity;

query.Include(x => x.Collection.Select(y => y.Property))

ตรวจสอบให้แน่ใจว่าได้เพิ่มusing System.Data.Entity;เพื่อรับเวอร์ชันของIncludeแลมบ์ดา


สำหรับ EF Core

ใช้วิธีการใหม่ ThenInclude

query.Include(x => x.Collection)
     .ThenInclude(x => x.Property);

1
ฉันไม่สามารถรวม () บน ApplicationsWithOverrideGroup มันไม่แสดงใน Intellisense
บ๊อบฮอร์น

ฉันใช้การแก้ไขของคุณไม่ได้เพราะ ApplicationsWithOverrideGroup เป็นรายการ แอปพลิเคชันเป็นคุณสมบัติของแต่ละรายการในรายการไม่ใช่ในรายการ
บ็อบฮอร์น

1
Ahhhh แต่ลิงค์ที่คุณให้ดูเหมือนจะให้คำตอบ ให้ฉันลองทำสิ่งนี้: หากต้องการรวมคอลเล็กชันจากนั้นหนึ่งคอลเล็กชันลงหนึ่งระดับ: query.Include (e => e.Level1Collection.Select (l1 => l1.Level2Collection))
บ๊อบฮอร์น

60
อย่าลืมรวม System.Data.Entity ในการใช้งาน มิฉะนั้น Intellisense จะให้เฉพาะรุ่นรวมถึง (เส้นทางของสตริง) ของวิธีการ
OJ Raqueño

5
@ ความเห็นที่คุณต้องเรียกIncludeสำหรับแต่ละทรัพย์สิน:Db.States.Include(state => state.Cities.Select(city => city.Customers).Include(state => state.Cities.Select(city => city.Vendors)
Diego Torres

72

ถ้าฉันเข้าใจคุณอย่างถูกต้องคุณจะถามเกี่ยวกับการรวมคุณสมบัติที่ซ้อนกัน ถ้าเป็นเช่นนั้น:

.Include(x => x.ApplicationsWithOverrideGroup.NestedProp)

หรือ

.Include("ApplicationsWithOverrideGroup.NestedProp")  

หรือ

.Include($"{nameof(ApplicationsWithOverrideGroup)}.{nameof(NestedProp)}")  

6
ขอบคุณฉันสามารถลอง ฉันหวังว่าจะสามารถพิมพ์สิ่งต่าง ๆ อย่างยิ่งและหลีกเลี่ยงตัวอักษรสตริง แต่ถ้าว่าวิธีการที่จะต้องมีการทำ ...
บ๊อบฮอร์น

1
คุณสนิท ฉันอาจไม่ชัดเจนว่า ApplicationsWithOverrideGroup เป็นรายการ ขอบคุณสำหรับการช่วยเหลือ!
บ๊อบฮอร์น

@Judo ฉันมีปัญหาเดียวกัน .. ในกรณีของฉันการซ้อนกันของชั้นลึกลงไปหลายชั้น ใน SQL ที่สร้างขึ้นฉันสามารถเห็นคอลัมน์ทั้งหมดกลับมาด้วยชื่อนามแฝงที่แตกต่างกันเป็น c1, c2 บางอย่างเช่นนั้น คำถามของฉันคือฉันจะสร้างคอลเลกชัน DTO ที่ซ้อนกันได้จากทุกอย่างของฉันได้อย่างไร :( .. อาจเป็นไปได้ที่คุณจะสามารถใช้ตัวอย่างข้างต้นได้เองโดยที่เราจะส่งคืนคอลัมน์ทั้งหมดโดยไม่มี DTO ที่กำหนดเอง )
TechQuery

2
อย่าลืมรวมSystem.Data.Entityในการใช้งาน มิฉะนั้น Intellisense จะให้เฉพาะInclude(string path)เวอร์ชันของวิธีการนั้น
AlexMelw

52

EF Core:การใช้ "ThenInclude" เพื่อโหลดระดับ mutiple: ตัวอย่างเช่น:

var blogs = context.Blogs
    .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
    .ToList();

53
ดูเหมือนว่านี่เป็น EF Core เท่านั้น
Chris Marisic

27
FYI: VS2017 Intellisense ไม่ทำงานแล้วรวมถึง เพียงพิมพ์ในวิธีที่คุณคิดว่าควรจะเป็นและข้อผิดพลาดการเน้นควรหายไป
JohnWrensby

4
ฉันต้องการเน้นความคิดเห็นของ @JohnWrensby บางครั้ง Intellisense อาจใช้เวลานานเป็นพิเศษในการจัดการกับ ThenInclude เหล่านี้ซึ่งอาจสร้างความสับสนให้กับผู้ใช้ใหม่ ฉันยังมีกรณีที่นิพจน์แลมบ์ดาแบบเรียบง่ายไม่ได้รับการจัดการอย่างถูกต้องจนกว่าคุณจะพิมพ์และคอมไพล์โดยไม่สนใจ "ข้อผิดพลาด" ที่แสดงใน VS
Pac0

@ Pac0 คุณช่วยชีวิตฉันไว้ การดิ้นรนเพื่อดูสิ่งของเด็กและทำไม่ได้
Bendram

28

ฉันทำผู้ช่วยเล็กน้อยสำหรับ Entity Framework 6 (.Net Core style) เพื่อรวมเอนทิตีย่อยด้วยวิธีที่ดี

อยู่ใน NuGet ทันที: ติดตั้งแพ็กเกจจากนั้นจึงรวม. Def6

using System.Data.Entity;

var thenInclude = context.One.Include(x => x.Twoes)
    .ThenInclude(x=> x.Threes)
    .ThenInclude(x=> x.Fours)
    .ThenInclude(x=> x.Fives)
    .ThenInclude(x => x.Sixes)
    .Include(x=> x.Other)
    .ToList();

แพคเกจที่มีอยู่บน GitHub


สวัสดีครับผมมีข้อยกเว้นที่รันไทม์ไม่สามารถโยน IncludableQueryable <ObservableCollection> เพื่อ IncludableQueryable <genericcollection>
user2475096

ฉันใช้ db ก่อนและฉันได้แก้ไขไฟล์ tt เพื่อรับ ObservableCollections สำหรับทุกหน่วยงานของฉันความช่วยเหลือใด ๆ ยินดีต้อนรับ
user2475096

2
@ lenny32 มีอะไรที่ต้องระวังด้วยส่วนขยายนี้หรือไม่
Aaron Hudon

โปรดทราบว่าสิ่งนี้ไม่จำเป็นถ้าคุณสมบัติที่คุณกำลังนำทางเป็นแบบหนึ่งต่อหนึ่งด้วย DbSet ที่คุณนำทางและคุณสามารถเชื่อมโยงDbSet<One>().Include(x => x.Two.Three.Four.Five.Six)กับข้อเสียเปรียบเพียงอย่างเดียวที่คุณกำลังประมวลผลผลิตภัณฑ์คาร์ทีเซียนและเพิ่มแบนด์วิดท์ที่อาจเกิดขึ้น
John Zabroski

23

เพิ่มเติมตัวอย่าง EFCore ใน MSDNแสดงให้เห็นว่าคุณสามารถทำสิ่งที่ค่อนข้างซับซ้อนบางอย่างกับและIncludeThenInclude

นี่เป็นตัวอย่างที่ดีของความซับซ้อนที่คุณจะได้รับ (นี่เป็นคำสั่งเดียวทั้งหมด!):

viewModel.Instructors = await _context.Instructors

      .Include(i => i.OfficeAssignment)

      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Enrollments)
                .ThenInclude(i => i.Student)

      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Department)

      .AsNoTracking()
      .OrderBy(i => i.LastName)
      .ToListAsync();

ดูว่าคุณจะเชื่อมโยงกันIncludeได้อย่างไรThenIncludeและเป็น 'รีเซ็ต' ให้คุณกลับไปที่ระดับเอนทิตีระดับบนสุด (ผู้สอน)

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

หมายเหตุแบบสอบถามจริงของคุณจะต้องติดแท็กที่ส่วนท้ายของห่วงโซ่Includeหรือ ThenIncludesต่อไปนี้ใช้ไม่ได้:

var query = _context.Instructors.AsQueryable();
query.Include(i => i.OfficeAssignment);

var first10Instructors = query.Take(10).ToArray();

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

AsNoTracking สามารถเร่งความเร็วให้มากขึ้นได้หากคุณไม่ได้ตั้งใจที่จะแก้ไขเอนทิตีและการบันทึกใหม่


มีวิธีรับทั้งการลงทะเบียนและแผนกโดยไม่ต้องทำซ้ำของคุณรวมถึง CourseAssignment และ Course หรือไม่? (จนถึงขณะนี้ก็ดูเหมือนว่า Api สามารถไปลึกกับ .ThenInclude หรือกลับไปที่ระดับด้านบนด้วย .Include แต่มีอะไรที่จะเข้าพักในระดับเดียวกัน?)
วิลเลียม Jockusch

หากคุณต้องการติดตามการโหลดแบบขี้เกียจสำหรับบล็อกของ EF Core 2.1.msdn.microsoft.com/dotnet/2018/02/02/ …แต่ถ้าคุณต้องการโหลดในระดับเดียวกันฉันคิดว่านี่เป็นการออกแบบ ฉันไม่แน่ใจว่าสิ่งที่คุณคิด - มันไม่จำเป็นต้องทำอะไรมากและลดสิ่งที่กลับมาจากฐานข้อมูล เอนทิตีอาจมีสิ่ง 'ระดับเดียวกัน' หนึ่งหรือสองอย่าง แต่อาจมี 50 รายการสำหรับโครงการขนาดใหญ่การที่ชัดเจนทำให้แอปของคุณเร็วขึ้นมาก
Simon_Weaver

นี่เป็นคำอธิบายที่ดีเกี่ยวกับแนวคิดของการรวม "การรีเซ็ต" ระดับกลับไปสู่ระดับเริ่มต้นอีกครั้ง ช่วยฉันห่อหัวของฉันรอบการสืบทอดของระบบ includ ไชโย!
Horizon

22

ฉันยังต้องใช้การรวมหลายรายการและที่ระดับ 3 ฉันต้องการคุณสมบัติหลายอย่าง

(from e in context.JobCategorySet
                      where e.Id == id &&
                            e.AgencyId == agencyId
                      select e)
                      .Include(x => x.JobCategorySkillDetails)
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.DurationType))
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.RuleType))
                      .Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt.RateType))
                      .FirstOrDefaultAsync();

นี่อาจช่วยใครซักคน :)


1
สามารถทำได้โดยไม่ต้องทำซ้ำ.Include(x => x.Shifts.Select(r => r.Rate).Select(rt => rt......
Multinerd

มันขึ้นอยู่กับว่าคุณอยากไปลึกแค่ไหน
dnxit

7

ให้ฉันระบุไว้อย่างชัดเจนว่าคุณสามารถใช้การโอเวอร์โหลดสตริงเพื่อรวมระดับที่ซ้อนกันโดยไม่คำนึงถึงความสัมพันธ์หลายหลากของความสัมพันธ์ที่เกี่ยวข้องหากคุณไม่คำนึงถึงการใช้ตัวอักษรสตริง:

query.Include("Collection.Property")

1
วิธีนี้เป็นประโยชน์สำหรับฉันที่จะคิดออกว่าจะเขียนโค้ดนี้ได้อย่างไรใน VB เพราะฉันไม่สามารถหาที่ไหนก็ได้หลังจากเวลาผ่านไปของ Google
Coder

มันใช้งานได้ดีสำหรับฉันฉันใช้มันเยอะมาก !!! มันยังทำงานร่วมกับคำสั่ง. SelectMany:query.SelectMany(x=>x.foos).Include("bar").Include("bar.docs")...
Ephie
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.