Hubungan satu ke satu
Hubungan satu-ke-satu digunakan ketika satu entitas dikaitkan dengan paling banyak satu entitas lain. Misalnya, memiliki Blog
satu BlogHeader
, dan itu BlogHeader
milik satu Blog
.
Dokumen ini disusun di sekitar banyak contoh. Contoh dimulai dengan kasus umum, yang juga memperkenalkan konsep. Contoh selanjutnya mencakup jenis konfigurasi yang kurang umum. Pendekatan yang baik di sini adalah memahami beberapa contoh dan konsep pertama, lalu pergi ke contoh selanjutnya berdasarkan kebutuhan spesifik Anda. Berdasarkan pendekatan ini, kita akan mulai dengan hubungan satu-ke-satu "wajib" dan "opsional" sederhana.
Tip
Kode untuk semua contoh di bawah ini dapat ditemukan di OneToOne.cs.
Diperlukan satu-ke-satu
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public BlogHeader? Header { get; set; } // Reference navigation to dependent
}
// Dependent (child)
public class BlogHeader
{
public int Id { get; set; }
public int BlogId { get; set; } // Required foreign key property
public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}
Hubungan satu-ke-satu terdiri dari:
- Satu atau beberapa properti kunci utama atau alternatif pada entitas utama. Contohnya,
Blog.Id
. - Satu atau beberapa properti kunci asing pada entitas dependen. Contohnya,
BlogHeader.BlogId
. - Secara opsional, navigasi referensi pada entitas utama yang mereferensikan entitas dependen. Contohnya,
Blog.Header
. - Secara opsional, navigasi referensi pada entitas dependen yang mereferensikan entitas utama. Contohnya,
BlogHeader.Blog
.
Tip
Tidak selalu jelas sisi mana dari hubungan satu-ke-satu yang harus menjadi prinsipal, dan sisi mana yang harus menjadi dependen. Beberapa pertimbangannya adalah:
- Jika tabel database untuk dua jenis sudah ada, maka tabel dengan kolom kunci asing harus dipetakan ke jenis dependen.
- Jenis biasanya merupakan jenis dependen jika tidak dapat ada secara logis tanpa jenis lainnya. Misalnya, tidak masuk akal untuk memiliki header untuk blog yang tidak ada, jadi
BlogHeader
secara alami jenis dependen. - Jika ada hubungan induk/anak alami, maka anak biasanya merupakan jenis dependen.
Jadi, untuk hubungan dalam contoh ini:
- Properti
BlogHeader.BlogId
kunci asing tidak dapat diubah ke null. Ini membuat hubungan "diperlukan" karena setiap dependen (BlogHeader
) harus terkait dengan beberapa prinsipal (Blog
), karena properti kunci asingnya harus diatur ke beberapa nilai. - Kedua entitas memiliki navigasi yang menunjuk ke entitas terkait di sisi lain hubungan.
Catatan
Hubungan yang diperlukan memastikan bahwa setiap entitas dependen harus dikaitkan dengan beberapa entitas utama. Namun, entitas utama selalu dapat ada tanpa entitas dependen apa pun. Artinya, hubungan yang diperlukan tidak menunjukkan bahwa akan selalu ada entitas dependen. Tidak ada cara dalam model EF, dan juga tidak ada cara standar dalam database relasional, untuk memastikan bahwa prinsipal dikaitkan dengan dependen. Jika ini diperlukan, maka harus diimplementasikan dalam logika aplikasi (bisnis). Lihat Navigasi yang diperlukan untuk informasi selengkapnya.
Tip
Hubungan dengan dua navigasi--satu dari dependen ke utama dan inversi dari prinsipal ke dependen--dikenal sebagai hubungan dua arah.
Hubungan ini ditemukan oleh konvensi. Yaitu:
Blog
ditemukan sebagai utama dalam hubungan, danBlogHeader
ditemukan sebagai dependen.BlogHeader.BlogId
ditemukan sebagai kunci asing dari dependen yang merujuk kunciBlog.Id
utama prinsipal. Hubungan ditemukan sesuai kebutuhan karenaBlogHeader.BlogId
tidak dapat diubah ke null.Blog.BlogHeader
ditemukan sebagai navigasi referensi.BlogHeader.Blog
ditemukan sebagai navigasi referensi.
Penting
Saat menggunakan jenis referensi C# nullable, navigasi dari dependen ke prinsipal harus dapat diubah ke null jika properti kunci asing dapat diubah ke null. Jika properti kunci asing tidak dapat diubah ke null, navigasi mungkin dapat diubah ke null atau tidak. Dalam hal ini, BlogHeader.BlogId
tidak dapat diubah ke null, dan BlogHeader.Blog
juga tidak dapat diubah ke null. Konstruksi = null!;
digunakan untuk menandai ini sebagai disengaja untuk pengkompilasi C#, karena EF biasanya mengatur instans Blog
dan tidak boleh null untuk hubungan yang dimuat sepenuhnya. Lihat Bekerja dengan Tipe Referensi Nullable untuk informasi selengkapnya.
Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Dalam contoh di atas, konfigurasi hubungan memulai jenis entitas utama (Blog
). Seperti semua hubungan, sama persis dengan dimulai dengan jenis entitas dependen (BlogHeader
) sebagai gantinya. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogHeader>()
.HasOne(e => e.Blog)
.WithOne(e => e.Header)
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Tidak satu pun dari opsi ini lebih baik daripada yang lain; keduanya menghasilkan konfigurasi yang sama persis.
Tip
Tidak perlu mengonfigurasi hubungan dua kali, sekali dimulai dari prinsipal, dan kemudian lagi dimulai dari dependen. Selain itu, mencoba mengonfigurasi bagian utama dan dependen dari hubungan secara terpisah umumnya tidak berfungsi. Pilih untuk mengonfigurasi setiap hubungan dari satu ujung atau yang lain lalu tulis kode konfigurasi hanya sekali.
Opsional satu-ke-satu
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public BlogHeader? Header { get; set; } // Reference navigation to dependent
}
// Dependent (child)
public class BlogHeader
{
public int Id { get; set; }
public int? BlogId { get; set; } // Optional foreign key property
public Blog? Blog { get; set; } // Optional reference navigation to principal
}
Ini sama dengan contoh sebelumnya, kecuali bahwa properti kunci asing dan navigasi ke prinsipal sekarang dapat diubah ke null. Ini membuat hubungan "opsional" karena dependen (BlogHeader
) tidak dapat terkait dengan prinsipal apa pun (Blog
) dengan mengatur properti kunci asing dan navigasinya ke null
.
Penting
Saat menggunakan jenis referensi C# nullable, properti navigasi dari dependen ke utama harus dapat diubah ke null jika properti kunci asing dapat diubah ke null. Dalam hal ini, BlogHeader.BlogId
nullable, jadi BlogHeader.Blog
harus nullable juga. Lihat Bekerja dengan Tipe Referensi Nullable untuk informasi selengkapnya.
Seperti sebelumnya, hubungan ini ditemukan oleh konvensi. Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired(false);
}
Diperlukan satu-ke-satu dengan kunci primer ke hubungan kunci primer
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public BlogHeader? Header { get; set; } // Reference navigation to dependent
}
// Dependent (child)
public class BlogHeader
{
public int Id { get; set; }
public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}
Tidak seperti hubungan satu-ke-banyak, akhir dependen dari hubungan satu-ke-satu dapat menggunakan properti kunci utama atau propertinya sebagai properti atau properti kunci asing. Ini sering disebut hubungan PK-ke-PK. Ini hanya dimungkinkan ketika jenis utama dan dependen memiliki jenis kunci utama yang sama, dan hubungan yang dihasilkan selalu diperlukan, karena kunci utama dependen tidak dapat diubah ke null.
Setiap hubungan satu-ke-satu di mana kunci asing tidak ditemukan oleh konvensi harus dikonfigurasi untuk menunjukkan ujung utama dan dependen dari hubungan. Ini biasanya dilakukan dengan panggilan ke HasForeignKey
. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>();
}
Tip
HasPrincipalKey
juga dapat digunakan untuk tujuan ini, tetapi melakukannya kurang umum.
Ketika tidak ada properti yang ditentukan dalam panggilan ke HasForeignKey
, dan kunci primer cocok, maka properti tersebut digunakan sebagai kunci asing. Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>(e => e.Id)
.IsRequired();
}
Diperlukan satu-ke-satu dengan kunci asing bayangan
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public BlogHeader? Header { get; set; } // Reference navigation to dependent
}
// Dependent (child)
public class BlogHeader
{
public int Id { get; set; }
public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}
Dalam beberapa kasus, Anda mungkin tidak menginginkan properti kunci asing dalam model Anda, karena kunci asing adalah detail tentang bagaimana hubungan diwakili dalam database, yang tidak diperlukan saat menggunakan hubungan dengan cara yang berorientasi objek murni. Namun, jika entitas akan diserialisasikan, misalnya untuk mengirim melalui kawat, maka nilai kunci asing dapat menjadi cara yang berguna untuk menjaga informasi hubungan tetap utuh ketika entitas tidak dalam bentuk objek. Oleh karena itu sering kali pragmatis untuk menyimpan properti kunci asing dalam jenis .NET untuk tujuan ini. Properti kunci asing dapat bersifat pribadi, yang sering menjadi kompromi yang baik untuk menghindari mengekspos kunci asing sambil memungkinkan nilainya untuk bepergian dengan entitas.
Mengikuti dari contoh sebelumnya, contoh ini menghapus properti kunci asing dari jenis entitas dependen. Namun, alih-alih menggunakan kunci primer, EF malah diinstruksikan untuk membuat properti kunci asing bayangan yang disebut BlogId
jenis int
.
Poin penting yang perlu diperhatikan di sini adalah bahwa jenis referensi C# nullable sedang digunakan, sehingga nullability navigasi dari dependen ke prinsipal digunakan untuk menentukan apakah properti kunci asing nullable atau tidak, dan oleh karena itu apakah hubungan bersifat opsional atau diperlukan. Jika jenis referensi nullable tidak digunakan, maka properti kunci asing bayangan akan dapat diubah ke null secara default membuat hubungan opsional secara default. Dalam hal ini, gunakan IsRequired
untuk memaksa properti kunci asing bayangan menjadi tidak dapat diubah ke null dan membuat hubungan diperlukan.
Hubungan ini lagi-lagi memerlukan beberapa konfigurasi untuk menunjukkan ujung utama dan dependen:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>("BlogId");
}
Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>("BlogId")
.IsRequired();
}
Opsional satu-ke-satu dengan kunci asing bayangan
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public BlogHeader? Header { get; set; } // Reference navigation to dependent
}
// Dependent (child)
public class BlogHeader
{
public int Id { get; set; }
public Blog? Blog { get; set; } // Optional reference navigation to principal
}
Seperti contoh sebelumnya, properti kunci asing telah dihapus dari jenis entitas dependen. Namun, tidak seperti contoh sebelumnya, kali ini properti kunci asing dibuat sebagai nullable karena jenis referensi C# nullable sedang digunakan dan navigasi pada jenis entitas dependen dapat diubah ke null. Hal ini membuat hubungan menjadi opsional.
Ketika jenis referensi C# nullable tidak digunakan, maka properti kunci asing akan, secara default, dibuat sebagai nullable. Ini berarti hubungan dengan properti bayangan yang dibuat secara otomatis bersifat opsional secara default.
Seperti sebelumnya, hubungan ini memerlukan beberapa konfigurasi untuk menunjukkan prinsipal dan dependen berakhir:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>("BlogId");
}
Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>("BlogId")
.IsRequired(false);
}
Satu-ke-satu tanpa navigasi ke prinsipal
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public BlogHeader? Header { get; set; } // Reference navigation to dependent
}
// Dependent (child)
public class BlogHeader
{
public int Id { get; set; }
public int BlogId { get; set; } // Required foreign key property
}
Untuk contoh ini, properti kunci asing telah diperkenalkan kembali, tetapi navigasi pada dependen telah dihapus.
Tip
Hubungan dengan hanya satu navigasi--satu dari dependen ke prinsipal atau satu dari prinsipal ke dependen, tetapi tidak keduanya--dikenal sebagai hubungan searah.
Hubungan ini ditemukan oleh konvensi, karena kunci asing ditemukan, sehingga menunjukkan sisi dependen. Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne()
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Perhatikan bahwa panggilan untuk WithOne
tidak memiliki argumen. Ini adalah cara untuk memberi tahu EF bahwa tidak ada navigasi dari BlogHeader
ke Blog
.
Jika konfigurasi dimulai dari entitas tanpa navigasi, maka jenis entitas di akhir hubungan lainnya harus ditentukan secara eksplisit menggunakan panggilan generik HasOne<>()
. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogHeader>()
.HasOne<Blog>()
.WithOne(e => e.Header)
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Satu-ke-satu tanpa navigasi ke utama dan dengan kunci asing bayangan
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public BlogHeader? Header { get; set; } // Reference navigation to dependent
}
// Dependent (child)
public class BlogHeader
{
public int Id { get; set; }
}
Contoh ini menggabungkan dua contoh sebelumnya dengan menghapus properti kunci asing dan navigasi pada dependen.
Seperti sebelumnya, hubungan ini memerlukan beberapa konfigurasi untuk menunjukkan prinsipal dan dependen berakhir:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne()
.HasForeignKey<BlogHeader>("BlogId")
.IsRequired();
}
Konfigurasi yang lebih lengkap dapat digunakan untuk mengonfigurasi navigasi dan nama kunci asing secara eksplisit, dengan panggilan yang sesuai ke IsRequired()
atau IsRequired(false)
sesuai kebutuhan. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne()
.HasForeignKey<BlogHeader>("BlogId")
.IsRequired();
}
Satu-ke-satu tanpa navigasi ke dependen
// Principal (parent)
public class Blog
{
public int Id { get; set; }
}
// Dependent (child)
public class BlogHeader
{
public int Id { get; set; }
public int BlogId { get; set; } // Required foreign key property
public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}
Dua contoh sebelumnya memiliki navigasi dari perwakilan ke dependen, tetapi tidak ada navigasi dari dependen ke utama. Untuk beberapa contoh berikutnya, navigasi pada dependen diperkenalkan kembali, sementara navigasi pada prinsipal dihapus sebagai gantinya.
Berdasarkan konvensi, EF akan memperlakukan ini sebagai hubungan satu-ke-banyak. Beberapa konfigurasi minimal diperlukan untuk membuatnya satu-ke-satu:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogHeader>()
.HasOne(e => e.Blog)
.WithOne();
}
Perhatikan lagi yang WithOne()
dipanggil tanpa argumen untuk menunjukkan bahwa tidak ada navigasi ke arah ini.
Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogHeader>()
.HasOne(e => e.Blog)
.WithOne()
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Jika konfigurasi dimulai dari entitas tanpa navigasi, maka jenis entitas di akhir hubungan lainnya harus ditentukan secara eksplisit menggunakan panggilan generik HasOne<>()
. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne<BlogHeader>()
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Satu-ke-satu tanpa navigasi
Terkadang, dapat berguna untuk mengonfigurasi hubungan tanpa navigasi. Hubungan seperti itu hanya dapat dimanipulasi dengan mengubah nilai kunci asing secara langsung.
// Principal (parent)
public class Blog
{
public int Id { get; set; }
}
// Dependent (child)
public class BlogHeader
{
public int Id { get; set; }
public int BlogId { get; set; } // Required foreign key property
}
Hubungan ini tidak ditemukan oleh konvensi, karena tidak ada navigasi yang menunjukkan bahwa kedua jenis tersebut terkait. Ini dapat dikonfigurasi secara eksplisit di OnModelCreating
. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne<BlogHeader>()
.WithOne();
}
Dengan konfigurasi ini, BlogHeader.BlogId
properti masih terdeteksi sebagai kunci asing berdasarkan konvensi, dan hubungan "diperlukan" karena properti kunci asing tidak dapat diubah ke null. Hubungan dapat dibuat "opsional" dengan membuat properti kunci asing nullable.
Konfigurasi eksplisit hubungan ini yang lebih lengkap adalah::
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne<BlogHeader>()
.WithOne()
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Satu-ke-satu dengan kunci alternatif
Dalam semua contoh sejauh ini, properti kunci asing pada dependen dibatasi ke properti kunci utama pada prinsipal. Kunci asing dapat dibatasi ke properti yang berbeda, yang kemudian menjadi kunci alternatif untuk jenis entitas utama. Misalnya:
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public int AlternateId { get; set; } // Alternate key as target of the BlogHeader.BlogId foreign key
public BlogHeader? Header { get; set; } // Reference navigation to dependent
}
// Dependent (child)
public class BlogHeader
{
public int Id { get; set; }
public int BlogId { get; set; } // Required foreign key property
public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}
Hubungan ini tidak ditemukan oleh konvensi, karena EF akan selalu, berdasarkan konvensi, membuat hubungan dengan kunci utama. Ini dapat dikonfigurasi secara eksplisit dalam OnModelCreating
menggunakan panggilan ke HasPrincipalKey
. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasPrincipalKey<Blog>(e => e.AlternateId);
}
HasPrincipalKey
dapat dikombinasikan dengan panggilan lain untuk secara eksplisit mengonfigurasi navigasi, properti kunci asing, dan sifat yang diperlukan/opsional. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasPrincipalKey<Blog>(e => e.AlternateId)
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Satu-ke-satu dengan kunci asing komposit
Dalam semua contoh sejauh ini, properti kunci utama atau alternatif dari prinsipal yang terdiri dari satu properti. Kunci primer atau alternatif juga dapat dibentuk membentuk lebih dari satu properti --ini dikenal sebagai "kunci komposit". Ketika prinsip hubungan memiliki kunci komposit, maka kunci asing dependen juga harus menjadi kunci komposit dengan jumlah properti yang sama. Misalnya:
// Principal (parent)
public class Blog
{
public int Id1 { get; set; } // Composite key part 1
public int Id2 { get; set; } // Composite key part 2
public BlogHeader? Header { get; set; } // Reference navigation to dependent
}
// Dependent (child)
public class BlogHeader
{
public int Id { get; set; }
public int BlogId1 { get; set; } // Required foreign key property part 1
public int BlogId2 { get; set; } // Required foreign key property part 2
public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}
Hubungan ini ditemukan oleh konvensi. Namun, itu hanya akan ditemukan jika kunci komposit telah dikonfigurasi secara eksplisit, karena kunci komposit tidak ditemukan secara otomatis. Contohnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasKey(e => new { e.Id1, e.Id2 });
}
Penting
Nilai kunci asing komposit dianggap null
jika salah satu nilai propertinya null. Kunci asing komposit dengan satu properti null dan non-null lainnya tidak akan dianggap cocok untuk kunci primer atau alternatif dengan nilai yang sama. Keduanya akan dipertimbangkan null
.
Baik HasForeignKey
dan HasPrincipalKey
dapat digunakan untuk secara eksplisit menentukan kunci dengan beberapa properti. Misalnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>(
nestedBuilder =>
{
nestedBuilder.HasKey(e => new { e.Id1, e.Id2 });
nestedBuilder.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasPrincipalKey<Blog>(e => new { e.Id1, e.Id2 })
.HasForeignKey<BlogHeader>(e => new { e.BlogId1, e.BlogId2 })
.IsRequired();
});
}
Tip
Dalam kode di atas, panggilan ke HasKey
dan HasOne
telah dikelompokkan bersama ke dalam penyusun berlapis. Penyusun berlapis menghapus kebutuhan untuk memanggil Entity<>()
beberapa kali untuk jenis entitas yang sama, tetapi secara fungsional setara dengan panggilan Entity<>()
beberapa kali.
Diperlukan satu-ke-satu tanpa penghapusan kaskade
// Principal (parent)
public class Blog
{
public int Id { get; set; }
public BlogHeader? Header { get; set; } // Reference navigation to dependent
}
// Dependent (child)
public class BlogHeader
{
public int Id { get; set; }
public int BlogId { get; set; } // Required foreign key property
public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}
Menurut konvensi, hubungan yang diperlukan dikonfigurasi untuk menghapus kaskade. Ini karena dependen tidak dapat ada dalam database setelah prinsipal dihapus. Database dapat dikonfigurasi untuk menghasilkan kesalahan, biasanya crash aplikasi, alih-alih secara otomatis menghapus baris dependen yang tidak dapat lagi ada. Ini memerlukan beberapa konfigurasi:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.OnDelete(DeleteBehavior.Restrict);
}
Mereferensikan sendiri satu-ke-satu
Dalam semua contoh sebelumnya, jenis entitas utama berbeda dari jenis entitas dependen. Ini tidak harus terjadi. Misalnya, dalam jenis di bawah ini, masing-masing Person
secara opsional terkait dengan yang lain Person
.
public class Person
{
public int Id { get; set; }
public int? HusbandId { get; set; } // Optional foreign key property
public Person? Husband { get; set; } // Optional reference navigation to principal
public Person? Wife { get; set; } // Reference navigation to dependent
}
Hubungan ini ditemukan oleh konvensi. Untuk kasus di mana navigasi, kunci asing, atau sifat hubungan yang diperlukan/opsional tidak ditemukan oleh konvensi, hal-hal ini dapat dikonfigurasi secara eksplisit. Contoh:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasOne(e => e.Husband)
.WithOne(e => e.Wife)
.HasForeignKey<Person>(e => e.HusbandId)
.IsRequired(false);
}
Catatan
Untuk hubungan referensi mandiri satu-ke-satu, karena jenis entitas utama dan dependen sama, menentukan jenis mana yang berisi kunci asing tidak mengklarifikasi akhir dependen. Dalam hal ini, navigasi yang ditentukan dalam HasOne
poin dari dependen ke utama, dan navigasi yang ditentukan dalam WithOne
poin dari utama ke dependen.