ตัวกรองคำสั่งสืบค้นส่วนกลางและเอนทิตีที่เป็นเจ้าของ
ใช้การแยกข้อมูลระหว่างผู้เช่าและการลบแบบซอฟต์ด้วยตัวกรองคำสืบค้นส่วนกลาง และแมปออบเจ็กต์ค่าโดยใช้ชนิดเอนทิตีที่เป็นเจ้าของ
ตัวกรองคำสั่งสืบค้นส่วนกลางและเอนทิตีที่เป็นเจ้าของ เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
ตัวกรองคิวรีส่วนกลางคืออะไร
ตัวกรองคิวรีส่วนกลางคือเงื่อนไข WHERE ที่นำไปใช้กับทุกคิวรี LINQ ของชนิดเอนทิตีที่กำหนดโดยอัตโนมัติ เหมาะอย่างยิ่งสำหรับการลบแบบไม่ลบจริง การรองรับหลายผู้เช่า และการรักษาความปลอดภัยระดับแถว โดยไม่ต้องเขียนเงื่อนไขซ้ำในทุกที่
การลบแบบไม่ลบจริงด้วยตัวกรองส่วนกลาง
เพิ่มแฟล็ก IsDeleted และกำหนดค่าตัวกรองส่วนกลาง เพื่อไม่ให้ส่งคืนระเบียนที่ถูกลบ เว้นแต่คุณจะสั่งละเว้นตัวกรองอย่างชัดเจน
// Entity with soft-delete flag
public class Post
{
public int Id { get; set; }
public string Title { get; set; } = "";
public bool IsDeleted { get; set; }
}
// Configure in OnModelCreating
modelBuilder.Entity<Post>()
.HasQueryFilter(p => !p.IsDeleted);
// All queries now implicitly add WHERE IsDeleted = 0:
var posts = await _db.Posts.ToListAsync();การรองรับหลายผู้เช่าด้วยตัวกรองส่วนกลาง
ใช้ตัวกรองที่อ่านผู้เช่าปัจจุบันจากบริการที่มีขอบเขต ทุกคิวรีจะถูกจำกัดให้ใช้เฉพาะข้อมูลของผู้เช่าปัจจุบันโดยอัตโนมัติ
public class AppDbContext : DbContext
{
private readonly ICurrentUserService _user;
public AppDbContext(DbContextOptions<AppDbContext> opt, ICurrentUserService user)
: base(opt) => _user = user;
protected override void OnModelCreating(ModelBuilder mb)
{
mb.Entity<Order>().HasQueryFilter(
o => o.TenantId == _user.TenantId);
}
}
// Every Order query automatically filters by TenantIdการละเว้นตัวกรองส่วนกลาง
บางครั้งคุณจำเป็นต้องข้ามตัวกรอง เช่น คิวรีของผู้ดูแลระบบที่ต้องดูข้อมูลของผู้เช่าทั้งหมด หรือการกู้คืนระเบียนที่ถูกลบแบบไม่ลบจริง ให้ใช้ IgnoreQueryFilters() เฉพาะกับคิวรีนั้น
// Admin: see ALL posts including deleted
var allPosts = await _db.Posts
.IgnoreQueryFilters()
.ToListAsync();
// Restore a specific deleted post
var deleted = await _db.Posts
.IgnoreQueryFilters()
.FirstOrDefaultAsync(p => p.Id == 42);
if (deleted is not null)
{
deleted.IsDeleted = false;
await _db.SaveChangesAsync();
}ตัวกรองบนคุณสมบัติการนำทาง
ตัวกรองส่วนกลางมีผลกับ JOIN ด้วย หากเอนทิตี Post มีตัวกรอง Post ที่เกี่ยวข้องซึ่งโหลดผ่าน Include ก็จะถูกกรองโดยอัตโนมัติเช่นกัน
// Blog with soft-delete filter on Posts
var blog = await _db.Blogs
.Include(b => b.Posts) // only non-deleted posts are included
.FirstOrDefaultAsync(b => b.Id == 1);
// blog.Posts contains only active posts — the filter applied in the JOINแนวคิดเกี่ยวกับชนิดเอนทิตีที่มีเจ้าของ
ชนิดเอนทิตีที่มีเจ้าของคือคลาสที่เป็นของเอนทิตีเจ้าของเพียงรายการเดียวเท่านั้น ชนิดเหล่านี้ไม่มีคีย์หลักและจัดเก็บอยู่ในตารางเดียวกันตามค่าเริ่มต้น เหมาะอย่างยิ่งสำหรับออบเจ็กต์ค่าใน DDD
public class Customer
{
public int Id { get; set; }
public string Name { get; set; } = "";
public Address BillingAddress { get; set; } = new();
public Address ShippingAddress { get; set; } = new();
}
public class Address
{
public string Street { get; set; } = "";
public string City { get; set; } = "";
public string ZipCode { get; set; } = "";
public string Country { get; set; } = "";
}การกำหนดค่าเอนทิตีที่มีเจ้าของ
ใช้ OwnsOne เพื่อกำหนดค่าเอนทิตีที่มีเจ้าของเพียงรายการเดียว โดย EF Core จะเติมชื่อคุณสมบัติการนำหน้าไว้หน้าชื่อคอลัมน์ตามค่าเริ่มต้น
modelBuilder.Entity<Customer>(entity =>
{
entity.OwnsOne(c => c.BillingAddress, addr =>
{
addr.Property(a => a.Street).HasMaxLength(300);
addr.Property(a => a.Country).HasMaxLength(2);
});
entity.OwnsOne(c => c.ShippingAddress);
});
// Table: Customers with columns:
// BillingAddress_Street, BillingAddress_City, ...
// ShippingAddress_Street, ShippingAddress_City, ...เอนทิตีที่มีเจ้าของในตารางแยกต่างหาก
ใช้ ToTable() ภายใน OwnsOne เพื่อจัดเก็บเอนทิตีที่มีเจ้าของไว้ในตารางของตนเอง เหมาะเมื่อข้อมูลมีขนาดใหญ่หรือเป็นข้อมูลที่อาจไม่มี
modelBuilder.Entity<Customer>().OwnsOne(
c => c.BillingAddress,
addr => addr.ToTable("CustomerBillingAddresses"));
// Now BillingAddress columns are in a separate table
// with a FK back to CustomerIdOwnsMany สำหรับคอลเลกชันของออบเจ็กต์ค่า
OwnsMany ใช้กำหนดค่าคอลเลกชันของเอนทิตีที่มีเจ้าของ เช่น รายการที่อยู่อีเมลหรือหมายเลขโทรศัพท์ซึ่งจัดเก็บไว้ในตารางลูก
public class Customer
{
public int Id { get; set; }
public IList<PhoneNumber> PhoneNumbers { get; set; } = new List<PhoneNumber>();
}
public class PhoneNumber
{
public string Number { get; set; } = "";
public string Type { get; set; } = "";
}
modelBuilder.Entity<Customer>()
.OwnsMany(c => c.PhoneNumbers,
phone => phone.ToTable("CustomerPhones"));ตารางต่อหนึ่งลำดับชั้นเทียบกับเอนทิตีที่มีเจ้าของ
ตัวกรองส่วนกลางยังทำงานได้ดีกับการสืบทอดแบบ TPH (Table-Per-Hierarchy) ซึ่งใช้คอลัมน์ตัวแยกชนิดเพื่อแยกชนิดย่อยออกจากกัน คุณสามารถใช้ตัวกรองตามค่าตัวแยกชนิดสำหรับคิวรีเฉพาะแต่ละชนิด
// Base entity
public abstract class Payment { public int Id { get; set; } }
public class CardPayment : Payment { public string CardLast4 { get; set; } = ""; }
public class BankPayment : Payment { public string IBAN { get; set; } = ""; }
// All Payment subtypes in one table with a Discriminator column
// No extra config needed — EF Core handles TPH by default
var cards = await _db.Set<CardPayment>().ToListAsync();
// SQL: SELECT * FROM Payments WHERE Discriminator = 'CardPayment'ในสถานการณ์จริง: การแยกข้อมูลผู้เช่าใน SaaS
การตั้งค่าระบบ SaaS ที่รองรับหลายผู้เช่าอย่างสมบูรณ์จะรวมตัวกรองคิวรีส่วนกลางเข้ากับบริบทผู้เช่าที่มีขอบเขต ทำให้ทุกคิวรีถูกจำกัดขอบเขตโดยอัตโนมัติโดยไม่ต้องมีโค้ดเฉพาะในแต่ละคิวรี
// Scoped tenant service
public class TenantContext
{
public Guid TenantId { get; set; }
}
// DbContext uses it
modelBuilder.Entity<Invoice>().HasQueryFilter(
inv => inv.TenantId == _tenantCtx.TenantId);
modelBuilder.Entity<Customer>().HasQueryFilter(
c => c.TenantId == _tenantCtx.TenantId);
// Queries are automatically scoped:
var invoices = await _db.Invoices.ToListAsync();
// WHERE TenantId = 'current-tenant-guid'ตรวจสอบความเข้าใจ
คุณเรียกเมธอดใดเพื่อข้ามตัวกรองคิวรีส่วนกลางชั่วคราวสำหรับคิวรีหนึ่ง ๆ
สรุป: ตัวกรองส่วนกลางและเอนทิตีที่มีเจ้าของ
ประเด็นสำคัญ:
- ตัวกรองคิวรีส่วนกลางจะเพิ่มเงื่อนไข WHERE ให้กับทุกคิวรีของเอนทิตีโดยอัตโนมัติ
- เหมาะอย่างยิ่งสำหรับการลบแบบไม่ลบจริง การรองรับหลายผู้เช่า และการรักษาความปลอดภัยระดับแถว
- ข้ามตัวกรองด้วย
IgnoreQueryFilters()สำหรับการดำเนินการของผู้ดูแลระบบหรือการกู้คืนข้อมูล - เอนทิตีที่มีเจ้าของใช้จำลองออบเจ็กต์ค่า ไม่มี PK และจัดเก็บอยู่ในตารางของเอนทิตีเจ้าของ
OwnsOne/OwnsManyใช้กำหนดความสัมพันธ์ของเอนทิตีที่มีเจ้าของ และใช้ToTable()เพื่อแยกตาราง
คำถามที่พบบ่อย
บทเรียน “ตัวกรองคำสั่งสืบค้นส่วนกลางและเอนทิตีที่เป็นเจ้าของ” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ตัวกรองคำสั่งสืบค้นส่วนกลางและเอนทิตีที่เป็นเจ้าของ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ตัวกรองคำสั่งสืบค้นส่วนกลางและเอนทิตีที่เป็นเจ้าของ”
ใช้การแยกข้อมูลระหว่างผู้เช่าและการลบแบบซอฟต์ด้วยตัวกรองคำสืบค้นส่วนกลาง และแมปออบเจ็กต์ค่าโดยใช้ชนิดเอนทิตีที่เป็นเจ้าของ คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “ตัวกรองคำสั่งสืบค้นส่วนกลางและเอนทิตีที่เป็นเจ้าของ” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม
ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การโหลดแบบทันที แบบขี้เกียจ และแบบชัดแจ้ง
- SQL ดิบ โพรซีเยอร์ที่จัดเก็บ และการแทรกค่า
- คำสั่งสืบค้นที่คอมไพล์แล้วและประสิทธิภาพ
- ตัวกรองคำสั่งสืบค้นส่วนกลางและเอนทิตีที่เป็นเจ้าของ