Bagikan melalui


Penghapusan Kaskade

Entity Framework Core (EF Core) mewakili hubungan menggunakan kunci asing. Entitas dengan kunci asing adalah entitas anak atau dependen dalam hubungan. Nilai kunci asing entitas ini harus cocok dengan nilai kunci utama (atau nilai kunci alternatif) dari entitas utama/induk terkait.

Jika entitas utama/induk dihapus, maka nilai kunci asing dependen/anak tidak akan lagi cocok dengan kunci utama atau alternatif dari prinsipal/induk apa pun . Ini adalah status yang tidak valid, dan akan menyebabkan pelanggaran batasan referensial di sebagian besar database.

Ada dua opsi untuk menghindari pelanggaran batasan referensial ini:

  1. Atur nilai FK ke null
  2. Hapus juga entitas dependen/anak

Opsi pertama hanya valid untuk hubungan opsional di mana properti kunci asing (dan kolom database yang dipetakan) harus nullable.

Opsi kedua berlaku untuk segala jenis hubungan dan dikenal sebagai "penghapusan kaskade".

Tip

Dokumen ini menjelaskan penghapusan bertingkat (dan menghapus yatim piatu) dari perspektif pembaruan database. Ini membuat penggunaan konsep yang berat diperkenalkan dalam Pelacakan Perubahan di EF Core dan Mengubah Kunci dan Navigasi Asing. Pastikan untuk sepenuhnya memahami konsep-konsep ini sebelum menangani materi di sini.

Tip

Anda dapat menjalankan dan men-debug ke semua kode dalam dokumen ini dengan mengunduh kode sampel dari GitHub.

Saat perilaku berskala terjadi

Penghapusan berjenjang diperlukan ketika entitas dependen/anak tidak dapat lagi dikaitkan dengan prinsipal/induknya saat ini. Ini dapat terjadi karena prinsipal/induk dihapus, atau dapat terjadi ketika prinsipal/induk masih ada tetapi dependen/anak tidak lagi terkait dengannya.

Menghapus prinsipal/induk

Pertimbangkan model sederhana ini di mana Blog adalah prinsipal/induk dalam hubungan dengan Post, yang merupakan dependen/anak. Post.BlogId adalah properti kunci asing, nilai yang harus cocok dengan Blog.Id kunci utama blog tempat posting berada.

public class Blog
{
    public int Id { get; set; }

    public string Name { get; set; }

    public IList<Post> Posts { get; } = new List<Post>();
}

public class Post
{
    public int Id { get; set; }

    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

Menurut konvensi, hubungan ini dikonfigurasi sebagai diperlukan, karena Post.BlogId properti kunci asing tidak dapat diubah ke null. Hubungan yang diperlukan dikonfigurasi untuk menggunakan penghapusan kaskade secara default. Lihat Hubungan untuk informasi selengkapnya tentang hubungan pemodelan.

Saat menghapus blog, semua postingan dihapus secara bertingkat. Misalnya:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();

context.Remove(blog);

context.SaveChanges();

SaveChanges menghasilkan SQL berikut, menggunakan SQL Server sebagai contoh:

-- Executed DbCommand (1ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p0='2'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (2ms) [Parameters=[@p1='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

Memisahkan hubungan

Daripada menghapus blog, kita justru dapat mengalihkan hubungan antara setiap posting dan blognya. Ini dapat dilakukan dengan mengatur navigasi Post.Blog referensi ke null untuk setiap posting:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();

foreach (var post in blog.Posts)
{
    post.Blog = null;
}

context.SaveChanges();

Hubungan juga dapat diputus dengan menghapus setiap postingan dari Blog.Posts navigasi koleksi:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();

blog.Posts.Clear();

context.SaveChanges();

Dalam kedua kasus, hasilnya sama: blog tidak dihapus, tetapi posting yang tidak lagi terkait dengan blog apa pun dihapus:

-- Executed DbCommand (1ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p0='2'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

Menghapus entitas yang tidak lagi terkait dengan prinsipal/dependen apa pun dikenal sebagai "menghapus yatim piatu".

Tip

Penghapusan kaskade dan penghapusan yatim piatu terkait erat. Keduanya mengakibatkan penghapusan entitas dependen/anak ketika hubungan dengan prinsipal/induk yang diperlukan terputus. Untuk penghapusan berjenjang, pemisahan ini terjadi karena prinsipal/induk itu sendiri dihapus. Untuk yatim piatu, entitas utama/induk masih ada, tetapi tidak lagi terkait dengan entitas dependen/anak.

Di mana perilaku berskala terjadi

Perilaku berkala dapat diterapkan ke:

  • Entitas yang dilacak oleh saat ini DbContext
  • Entitas dalam database yang belum dimuat ke dalam konteks

Penghapusan bertingkat entitas terlacak

EF Core selalu menerapkan perilaku bertingkat yang dikonfigurasi ke entitas yang dilacak. Ini berarti bahwa jika aplikasi memuat semua entitas dependen/anak yang relevan ke dalam DbContext, seperti yang ditunjukkan pada contoh di atas, maka perilaku berjenjang akan diterapkan dengan benar terlepas dari bagaimana database dikonfigurasi.

Tip

Waktu yang tepat ketika perilaku bertingkat terjadi pada entitas yang dilacak dapat dikontrol menggunakan ChangeTracker.CascadeDeleteTiming dan ChangeTracker.DeleteOrphansTiming. Lihat Mengubah Kunci Asing dan Navigasi untuk informasi selengkapnya.

Menghapus kaskade dalam database

Banyak sistem database juga menawarkan perilaku berskala yang dipicu ketika entitas dihapus dalam database. EF Core mengonfigurasi perilaku ini berdasarkan perilaku penghapusan kaskade dalam model EF Core saat database dibuat menggunakan EnsureCreated atau migrasi EF Core. Misalnya, menggunakan model di atas, tabel berikut dibuat untuk postingan saat menggunakan SQL Server:

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Title] nvarchar(max) NULL,
    [Content] nvarchar(max) NULL,
    [BlogId] int NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]) ON DELETE CASCADE
);

Perhatikan bahwa batasan kunci asing yang menentukan hubungan antara blog dan posting dikonfigurasi dengan ON DELETE CASCADE.

Jika kita tahu bahwa database dikonfigurasi seperti ini, maka kita dapat menghapus blog tanpa terlebih dahulu memuat posting dan database akan mengurus penghapusan semua posting yang terkait dengan blog tersebut. Misalnya:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).First();

context.Remove(blog);

context.SaveChanges();

Perhatikan bahwa tidak Include ada untuk posting, sehingga tidak dimuat. SaveChanges dalam hal ini hanya akan menghapus blog, karena itulah satu-satunya entitas yang dilacak:

-- Executed DbCommand (6ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

Ini akan mengakibatkan pengecualian jika batasan kunci asing dalam database tidak dikonfigurasi untuk penghapusan kaskade. Namun, dalam hal ini posting dihapus oleh database karena telah dikonfigurasi dengan ON DELETE CASCADE saat dibuat.

Catatan

Database biasanya tidak memiliki cara untuk menghapus yatim piatu secara otomatis. Ini karena sementara EF Core mewakili hubungan menggunakan navigasi serta kunci asing, database hanya memiliki kunci asing dan tidak ada navigasi. Ini berarti bahwa biasanya tidak mungkin untuk memutuskan hubungan tanpa memuat kedua sisi ke dalam DbContext.

Catatan

Database dalam memori EF Core saat ini tidak mendukung penghapusan kaskade dalam database.

Peringatan

Jangan mengonfigurasi penghapusan bertingkat dalam database saat menghapus sementara entitas. Ini dapat menyebabkan entitas secara tidak sengaja benar-benar dihapus alih-alih dihapus sementara.

Batasan kaskade database

Beberapa database, terutama SQL Server, memiliki batasan pada perilaku bertingkat yang membentuk siklus. Misalnya, pertimbangkan model berikut:

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }

    public IList<Post> Posts { get; } = new List<Post>();

    public int OwnerId { get; set; }
    public Person Owner { get; set; }
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }

    public int AuthorId { get; set; }
    public Person Author { get; set; }
}

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }

    public IList<Post> Posts { get; } = new List<Post>();

    public Blog OwnedBlog { get; set; }
}

Model ini memiliki tiga hubungan, semuanya diperlukan dan oleh karena itu dikonfigurasi untuk menghapus bertahap menurut konvensi:

  • Menghapus blog akan menghapus semua posting terkait
  • Menghapus penulis postingan akan menyebabkan postingan yang ditulis dihapus secara bertingkat
  • Menghapus pemilik blog akan menyebabkan blog dihapus secara kaskade

Ini semua wajar (jika sedikit draconian dalam kebijakan manajemen blog!) tetapi mencoba membuat database SQL Server dengan kaskade ini dikonfigurasi menghasilkan pengecualian berikut:

Microsoft.Data.SqlClient.SqlException (0x80131904): Memperkenalkan batasan KUNCI ASING 'FK_Posts_Person_AuthorId' pada tabel 'Posts' dapat menyebabkan siklus atau beberapa jalur kaskade. Tentukan ON DELETE NO ACTION atau ON UPDATE NO ACTION, atau ubah batasan KUNCI ASING lainnya.

Ada dua cara untuk menangani situasi ini:

  1. Ubah satu atau beberapa hubungan agar tidak menghapus kaskade.
  2. Konfigurasikan database tanpa satu atau beberapa penghapusan bertingkat ini, lalu pastikan semua entitas dependen dimuat sehingga EF Core dapat melakukan perilaku bertingkat.

Mengambil pendekatan pertama dengan contoh kami, kita dapat membuat hubungan pasca-blog opsional dengan memberinya properti kunci asing yang dapat diubah ke null:

public int? BlogId { get; set; }

Hubungan opsional memungkinkan postingan ada tanpa blog, yang berarti penghapusan bertingkat tidak akan lagi dikonfigurasi secara default. Ini berarti tidak ada lagi siklus dalam tindakan berkala, dan database dapat dibuat tanpa kesalahan di SQL Server.

Dengan mengambil pendekatan kedua, kita dapat menjaga hubungan pemilik blog yang diperlukan dan dikonfigurasi untuk penghapusan bertingkat, tetapi membuat konfigurasi ini hanya berlaku untuk entitas yang dilacak, bukan database:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Blog>()
        .HasOne(e => e.Owner)
        .WithOne(e => e.OwnedBlog)
        .OnDelete(DeleteBehavior.ClientCascade);
}

Sekarang apa yang terjadi jika kita memuat seseorang dan blog yang mereka miliki, lalu menghapus orang tersebut?

using var context = new BlogsContext();

var owner = context.People.Single(e => e.Name == "ajcvickers");
var blog = context.Blogs.Single(e => e.Owner == owner);

context.Remove(owner);

context.SaveChanges();

EF Core akan mengapit penghapusan pemilik sehingga blog juga dihapus:

-- Executed DbCommand (8ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (2ms) [Parameters=[@p1='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [People]
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

Namun, jika blog tidak dimuat ketika pemilik dihapus:

using var context = new BlogsContext();

var owner = context.People.Single(e => e.Name == "ajcvickers");

context.Remove(owner);

context.SaveChanges();

Kemudian pengecualian akan dilemparkan karena pelanggaran batasan kunci asing dalam database:

Microsoft.Data.SqlClient.SqlException: Pernyataan DELETE bertentangan dengan batasan REFERENSI "FK_Blogs_Orang_OwnerId". Konflik terjadi dalam database "Scratch", tabel "dbo. Blogs", kolom 'OwnerId'. Pernyataan ini telah dihapus.

Cascading nulls

Hubungan opsional memiliki properti kunci asing null yang dipetakan ke kolom database nullable. Ini berarti bahwa nilai kunci asing dapat diatur ke null ketika prinsipal/induk saat ini dihapus atau diputus dari dependen/anak.

Mari kita lihat lagi contoh dari Ketika perilaku berjenjang terjadi, tetapi kali ini dengan hubungan opsional yang diwakili oleh properti kunci asing yang dapat Post.BlogId diubah ke null:

public int? BlogId { get; set; }

Properti kunci asing ini akan diatur ke null untuk setiap postingan ketika blog terkait dihapus. Misalnya, kode ini, yang sama seperti sebelumnya:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();

context.Remove(blog);

context.SaveChanges();

Sekarang akan menghasilkan pembaruan database berikut ketika SaveChanges dipanggil:

-- Executed DbCommand (2ms) [Parameters=[@p1='1', @p0=NULL (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p1='2', @p0=NULL (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

-- Executed DbCommand (1ms) [Parameters=[@p2='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p2;
SELECT @@ROWCOUNT;

Demikian juga, jika hubungan terputus menggunakan salah satu contoh dari atas:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();

foreach (var post in blog.Posts)
{
    post.Blog = null;
}

context.SaveChanges();

Atau:

using var context = new BlogsContext();

var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();

blog.Posts.Clear();

context.SaveChanges();

Kemudian postingan diperbarui dengan nilai kunci asing null saat SaveChanges dipanggil:

-- Executed DbCommand (2ms) [Parameters=[@p1='1', @p0=NULL (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p1='2', @p0=NULL (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

Lihat Mengubah Kunci Asing dan Navigasi untuk informasi selengkapnya tentang cara EF Core mengelola kunci asing dan navigasi saat nilainya diubah.

Catatan

Perbaikan hubungan seperti ini telah menjadi perilaku default Kerangka Kerja Entitas sejak versi pertama pada 2008. Sebelum EF Core tidak memiliki nama dan tidak mungkin diubah. Sekarang dikenal sebagaimana ClientSetNull dijelaskan di bagian berikutnya.

Database juga dapat dikonfigurasi untuk mengapit null seperti ini ketika prinsipal/induk dalam hubungan opsional dihapus. Namun, ini jauh kurang umum daripada menggunakan penghapusan berskala dalam database. Menggunakan penghapusan berskala dan cascading null dalam database pada saat yang sama hampir selalu menghasilkan siklus hubungan saat menggunakan SQL Server. Lihat bagian berikutnya untuk informasi selengkapnya tentang mengonfigurasi null bertingkat.

Mengonfigurasi perilaku bertingkat

Tip

Pastikan untuk membaca bagian di atas sebelum datang ke sini. Opsi konfigurasi kemungkinan tidak akan masuk akal jika materi sebelumnya tidak dipahami.

Perilaku kaskade dikonfigurasi per hubungan menggunakan OnDelete metode di OnModelCreating. Misalnya:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Blog>()
        .HasOne(e => e.Owner)
        .WithOne(e => e.OwnedBlog)
        .OnDelete(DeleteBehavior.ClientCascade);
}

Lihat Hubungan untuk informasi selengkapnya tentang mengonfigurasi hubungan antar jenis entitas.

OnDelete menerima nilai dari, diakui membingungkan, DeleteBehavior enum. Enum ini mendefinisikan perilaku EF Core pada entitas yang dilacak, dan konfigurasi penghapusan bertingkat dalam database ketika EF digunakan untuk membuat skema.

Dampak pada skema database

Tabel berikut menunjukkan hasil setiap OnDelete nilai pada batasan kunci asing yang dibuat oleh migrasi EF Core atau EnsureCreated.

DeleteBehavior Dampak pada skema database
Cascade ON DELETE CASCADE
Membatasi PADA HAPUS BATASAN
NoAction default database
SetNull PADA HAPUS SET NULL
ClientSetNull default database
ClientCascade default database
ClientNoAction default database

Perilaku ON DELETE NO ACTION (default database) dan ON DELETE RESTRICT dalam database relasional biasanya identik atau sangat mirip. Terlepas dari apa yang NO ACTION mungkin menyiratkan, kedua opsi ini menyebabkan kendala referensial diberlakukan. Perbedaannya, ketika ada, adalah ketika database memeriksa batasan. Periksa dokumentasi database Anda untuk mengetahui perbedaan spesifik antara ON DELETE NO ACTION dan ON DELETE RESTRICT pada sistem database Anda.

SQL Server tidak mendukung ON DELETE RESTRICT, jadi ON DELETE NO ACTION digunakan sebagai gantinya.

Satu-satunya nilai yang akan menyebabkan perilaku berskala pada database adalah Cascade dan SetNull. Semua nilai lain akan mengonfigurasi database untuk tidak menyimpan perubahan apa pun.

Dampak pada perilaku SaveChanges

Tabel di bagian berikut mencakup apa yang terjadi pada entitas dependen/anak ketika prinsipal/induk dihapus, atau hubungannya dengan entitas dependen/anak terputus. Setiap tabel mencakup salah satu dari:

  • Hubungan opsional (FK nullable) dan diperlukan (FK yang tidak dapat diubah ke null)
  • Ketika dependen/anak dimuat dan dilacak oleh DbContext dan ketika mereka hanya ada di database

Hubungan yang diperlukan dengan dependen/anak dimuat

DeleteBehavior Saat menghapus prinsipal/induk Saat memisahkan dari prinsipal/induk
Cascade Dependen dihapus oleh EF Core Dependen dihapus oleh EF Core
Membatasi InvalidOperationException InvalidOperationException
NoAction InvalidOperationException InvalidOperationException
SetNull SqlException pada pembuatan database SqlException pada pembuatan database
ClientSetNull InvalidOperationException InvalidOperationException
ClientCascade Dependen dihapus oleh EF Core Dependen dihapus oleh EF Core
ClientNoAction DbUpdateException InvalidOperationException

Catatan:

  • Default untuk hubungan yang diperlukan seperti ini adalah Cascade.
  • Menggunakan apa pun selain penghapusan kaskade untuk hubungan yang diperlukan akan menghasilkan pengecualian saat SaveChanges dipanggil.
    • Biasanya, ini adalah InvalidOperationException dari EF Core karena status tidak valid terdeteksi dalam turunan/dependen yang dimuat.
    • ClientNoAction memaksa EF Core untuk tidak memeriksa dependen perbaikan sebelum mengirimkannya ke database, jadi dalam hal ini database melemparkan pengecualian, yang kemudian dibungkus dalam DbUpdateException oleh SaveChanges.
    • SetNull ditolak saat membuat database karena kolom kunci asing tidak dapat diubah ke null.
  • Karena dependen/anak dimuat, dependen/anak selalu dihapus oleh EF Core, dan tidak pernah dibiarkan database dihapus.

Hubungan yang diperlukan dengan dependen/anak tidak dimuat

DeleteBehavior Saat menghapus prinsipal/induk Saat memisahkan dari prinsipal/induk
Cascade Dependen dihapus oleh database T/A
Membatasi DbUpdateException T/A
NoAction DbUpdateException T/A
SetNull SqlException pada pembuatan database T/A
ClientSetNull DbUpdateException T/A
ClientCascade DbUpdateException T/A
ClientNoAction DbUpdateException T/A

Catatan:

  • Memisahkan hubungan tidak valid di sini karena dependen/anak tidak dimuat.
  • Default untuk hubungan yang diperlukan seperti ini adalah Cascade.
  • Menggunakan apa pun selain penghapusan kaskade untuk hubungan yang diperlukan akan menghasilkan pengecualian saat SaveChanges dipanggil.
    • Biasanya, ini karena DbUpdateException dependen/anak tidak dimuat, dan karenanya status tidak valid hanya dapat dideteksi oleh database. SaveChanges kemudian membungkus pengecualian database dalam DbUpdateException.
    • SetNull ditolak saat membuat database karena kolom kunci asing tidak dapat diubah ke null.

Hubungan opsional dengan dependen/anak dimuat

DeleteBehavior Saat menghapus prinsipal/induk Saat memisahkan dari prinsipal/induk
Cascade Dependen dihapus oleh EF Core Dependen dihapus oleh EF Core
Membatasi FK Dependen diatur ke null oleh EF Core FK Dependen diatur ke null oleh EF Core
NoAction FK Dependen diatur ke null oleh EF Core FK Dependen diatur ke null oleh EF Core
SetNull FK Dependen diatur ke null oleh EF Core FK Dependen diatur ke null oleh EF Core
ClientSetNull FK Dependen diatur ke null oleh EF Core FK Dependen diatur ke null oleh EF Core
ClientCascade Dependen dihapus oleh EF Core Dependen dihapus oleh EF Core
ClientNoAction DbUpdateException FK Dependen diatur ke null oleh EF Core

Catatan:

  • Default untuk hubungan opsional seperti ini adalah ClientSetNull.
  • Dependen/anak tidak pernah dihapus kecuali Cascade atau ClientCascade dikonfigurasi.
  • Semua nilai lain menyebabkan FK dependen diatur ke null oleh EF Core...
    • ... kecuali ClientNoAction yang memberi tahu EF Core untuk tidak menyentuh kunci asing dependen/anak ketika prinsipal/induk dihapus. Oleh karena itu, database melemparkan pengecualian, yang dibungkus sebagai DbUpdateException oleh SaveChanges.

Hubungan opsional dengan dependen/anak yang tidak dimuat

DeleteBehavior Saat menghapus prinsipal/induk Saat memisahkan dari prinsipal/induk
Cascade Dependen dihapus oleh database T/A
Membatasi DbUpdateException T/A
NoAction DbUpdateException T/A
SetNull FK Dependen diatur ke null menurut database T/A
ClientSetNull DbUpdateException T/A
ClientCascade DbUpdateException T/A
ClientNoAction DbUpdateException T/A

Catatan:

  • Memisahkan hubungan tidak valid di sini karena dependen/anak tidak dimuat.
  • Default untuk hubungan opsional seperti ini adalah ClientSetNull.
  • Dependen/children harus dimuat untuk menghindari pengecualian database kecuali database telah dikonfigurasi untuk menghapus atau null.