0Pricing
C# Academy · บทเรียน

ความสัมพันธ์: หนึ่งต่อหลายและหลายต่อหลาย

จำลองและสืบค้นความสัมพันธ์แบบหนึ่งต่อหลายและหลายต่อหลายด้วยคุณสมบัติการนำทางและ Fluent API

ความสัมพันธ์: หนึ่งต่อหลายและหลายต่อหลาย เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน

ความสัมพันธ์ใน EF Core

EF Core จำลองความสัมพันธ์ระหว่างเอนทิตีไว้สามประเภท ได้แก่ หนึ่งต่อหนึ่ง หนึ่งต่อหลาย และ หลายต่อหลาย แต่ละประเภทแสดงด้วยพร็อพเพอร์ตีการนำทาง และสามารถกำหนดค่าเพิ่มเติมด้วย Fluent API ได้

หนึ่งต่อหลาย: การตั้งค่าเอนทิตี

Blog หนึ่งรายการมี Post ได้หลายรายการ เอนทิตีรอง (Post) จะเก็บคีย์นอกไว้ พร็อพเพอร์ตีการนำทางของทั้งสองฝั่งช่วยให้เดินทางไปตามความสัมพันธ์ได้ทั้งสองทิศทาง

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public ICollection<Post> Posts { get; set; } = new List<Post>();
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; } = "";
    public int BlogId { get; set; }        // FK
    public Blog Blog { get; set; } = null!; // nav property
}

Fluent API: หนึ่งต่อหลาย

EF Core อนุมานความสัมพันธ์ตามแบบแผนได้ แต่คุณสามารถกำหนดอย่างชัดเจนด้วย Fluent API เพื่อให้เข้าใจได้ง่ายขึ้นและกำหนดลักษณะการลบแบบส่งต่อ

protected override void OnModelCreating(ModelBuilder mb)
{
    mb.Entity<Blog>()
      .HasMany(b => b.Posts)
      .WithOne(p => p.Blog)
      .HasForeignKey(p => p.BlogId)
      .OnDelete(DeleteBehavior.Cascade);
}

การสอบถามด้วย Include (การโหลดล่วงหน้า)

ใช้ Include เพื่อโหลดข้อมูลที่เกี่ยวข้องในการสอบถามเดียวกัน ใช้ ThenInclude เพื่อเจาะลึกลงไปในกราฟการนำทาง

// Load blog with all its posts
var blog = await _db.Blogs
    .Include(b => b.Posts)
    .FirstOrDefaultAsync(b => b.Id == 1);

// Load posts with their comments too
var blogs = await _db.Blogs
    .Include(b => b.Posts)
        .ThenInclude(p => p.Comments)
    .ToListAsync();

หลายต่อหลายโดยไม่มีเอนทิตีเชื่อม

EF Core 5+ รองรับความสัมพันธ์แบบหลายต่อหลายโดยไม่ต้องมีเอนทิตีเชื่อมที่กำหนดเอง โดยจะสร้างตารางเชื่อมโดยอัตโนมัติจากชื่อประเภทเอนทิตี

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public ICollection<Course> Courses { get; set; } = new List<Course>();
}

public class Course
{
    public int Id { get; set; }
    public string Title { get; set; } = "";
    public ICollection<Student> Students { get; set; } = new List<Student>();
}
// EF creates StudentCourse join table automatically

หลายต่อหลายที่มีเอนทิตีเชื่อม

เมื่อ ตารางเชื่อมต้องมีคอลัมน์เพิ่มเติม เช่น วันที่ลงทะเบียนหรือเกรด ให้สร้างเอนทิตีเชื่อมโดยเฉพาะพร้อมคีย์หลักแบบผสม

public class Enrollment
{
    public int StudentId { get; set; }
    public Student Student { get; set; } = null!;
    public int CourseId { get; set; }
    public Course Course { get; set; } = null!;
    public DateTime EnrolledAt { get; set; }
    public decimal? Grade { get; set; }
}

// Fluent API config:
mb.Entity<Enrollment>().HasKey(e => new { e.StudentId, e.CourseId });
mb.Entity<Enrollment>().HasOne(e => e.Student).WithMany(s => s.Enrollments);
mb.Entity<Enrollment>().HasOne(e => e.Course).WithMany(c => c.Enrollments);

การแทรกข้อมูลที่เกี่ยวข้อง

เพิ่มเอนทิตีที่เกี่ยวข้องลงในคอลเลกชันของเอนทิตีแม่ก่อนเรียกใช้ SaveChangesAsync EF Core จะจัดการค่าของ FK โดยอัตโนมัติ

var blog = new Blog { Name = "Dev Notes" };
blog.Posts.Add(new Post { Title = "Hello EF Core" });
blog.Posts.Add(new Post { Title = "Migrations Deep Dive" });

_db.Blogs.Add(blog);
await _db.SaveChangesAsync();
// Inserts Blog + 2 Posts with correct BlogId FK

การกรองข้อมูลที่เกี่ยวข้อง

คุณสามารถกรองด้วยพร็อพเพอร์ตีการนำทางใน LINQ ได้ EF Core จะแปลงการกรองเหล่านี้เป็นคำสั่ง SQL JOIN โดยอัตโนมัติ

// Blogs that have at least one published post
var popularBlogs = await _db.Blogs
    .Where(b => b.Posts.Any(p => p.IsPublished))
    .Select(b => new { b.Name, PostCount = b.Posts.Count(p => p.IsPublished) })
    .OrderByDescending(b => b.PostCount)
    .ToListAsync();

การลบแบบส่งต่อ

เมื่อใช้ DeleteBehavior.Cascade การลบเอนทิตีหลัก (Blog) จะลบเอนทิตีรองทั้งหมด (Posts) โดยอัตโนมัติ ตัวเลือกอื่น ได้แก่ SetNull, Restrict, NoAction

// Delete a blog and all its posts
var blog = await _db.Blogs.FindAsync(1);
_db.Blogs.Remove(blog!);
await _db.SaveChangesAsync();
// Cascade: all Posts with BlogId=1 are also deleted

เอนทิตีที่เป็นเจ้าของสำหรับออบเจ็กต์ค่า

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

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public Address ShippingAddress { get; set; } = new();
}

public class Address
{
    public string Street { get; set; } = "";
    public string City { get; set; } = "";
    public string Country { get; set; } = "";
}

// Fluent API:
mb.Entity<Customer>().OwnsOne(c => c.ShippingAddress);

กรณีใช้งานจริง: คำสั่งซื้อพร้อมรายการสินค้า

รูปแบบอีคอมเมิร์ซที่สมบูรณ์: คำสั่งซื้อ (เอนทิตีหลัก) ที่มี OrderLines หลายรายการ (เอนทิตีรอง) โดยแต่ละรายการอ้างอิงถึง Product

var order = await _db.Orders
    .Include(o => o.Lines)
        .ThenInclude(l => l.Product)
    .FirstOrDefaultAsync(o => o.Id == orderId);

if (order is null) return Results.NotFound();

var summary = new
{
    OrderId = order.Id,
    Total   = order.Lines.Sum(l => l.Quantity * l.Product.Price),
    Items   = order.Lines.Select(l => new
    {
        l.Product.Name,
        l.Quantity,
        LineTotal = l.Quantity * l.Product.Price
    })
};
return Results.Ok(summary);

ตรวจสอบความเข้าใจ

EF Core จะสร้างตารางเชื่อมโดยอัตโนมัติโดยไม่ต้องมีเอนทิตีเชื่อมที่กำหนดเองเมื่อใด

ทบทวน: ความสัมพันธ์ใน EF Core

ประเด็นสำคัญ:

  • หนึ่งต่อหลาย: FK อยู่ที่เอนทิตีรอง มีการนำทางทั้งสองฝั่ง และใช้ Include เพื่อโหลดข้อมูล
  • หลายต่อหลาย (แบบง่าย): มีการนำทาง ICollection สองรายการ — EF Core จะสร้างตารางเชื่อมโดยอัตโนมัติ
  • หลายต่อหลาย (มีข้อมูลเพิ่มเติม): ใช้เอนทิตีเชื่อมโดยเฉพาะพร้อม PK แบบผสม
  • การลบแบบส่งต่อจะควบคุมว่าจะเกิดอะไรขึ้นกับเอนทิตีรองเมื่อเอนทิตีหลักถูกลบ
  • เอนทิตีที่เป็นเจ้าของใช้จำลองออบเจ็กต์ค่าในตารางเดียวกับเอนทิตีเจ้าของ

คำถามที่พบบ่อย

บทเรียน “ความสัมพันธ์: หนึ่งต่อหลายและหลายต่อหลาย” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ความสัมพันธ์: หนึ่งต่อหลายและหลายต่อหลาย” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ความสัมพันธ์: หนึ่งต่อหลายและหลายต่อหลาย”

จำลองและสืบค้นความสัมพันธ์แบบหนึ่งต่อหลายและหลายต่อหลายด้วยคุณสมบัติการนำทางและ Fluent API คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “ความสัมพันธ์: หนึ่งต่อหลายและหลายต่อหลาย” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม

ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. พื้นฐาน DbContext และ DbSet
  2. การย้ายข้อมูลและการจัดการสคีมา
  3. การดำเนินการ CRUD ด้วย EF Core
  4. ความสัมพันธ์: หนึ่งต่อหลายและหลายต่อหลาย
← กลับไปที่ C# Academy