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

ตัวกรองคำสั่งสืบค้นส่วนกลางและเอนทิตีที่เป็นเจ้าของ

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

ตัวกรองคำสั่งสืบค้นส่วนกลางและเอนทิตีที่เป็นเจ้าของ เป็นบทเรียน 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 CustomerId

OwnsMany สำหรับคอลเลกชันของออบเจ็กต์ค่า

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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

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

  1. การโหลดแบบทันที แบบขี้เกียจ และแบบชัดแจ้ง
  2. SQL ดิบ โพรซีเยอร์ที่จัดเก็บ และการแทรกค่า
  3. คำสั่งสืบค้นที่คอมไพล์แล้วและประสิทธิภาพ
  4. ตัวกรองคำสั่งสืบค้นส่วนกลางและเอนทิตีที่เป็นเจ้าของ
← กลับไปที่ C# Academy