Navigasi hubungan
Hubungan EF Core didefinisikan oleh kunci asing. Navigasi berlapis di atas kunci asing untuk memberikan tampilan alami berorientasi objek untuk membaca dan memanipulasi hubungan. Dengan menggunakan navigasi, aplikasi dapat bekerja dengan grafik entitas tanpa khawatir dengan apa yang terjadi pada nilai kunci asing.
Penting
Beberapa hubungan tidak dapat berbagi navigasi. Kunci asing apa pun dapat dikaitkan dengan paling banyak satu navigasi dari utama ke dependen, dan paling banyak satu navigasi dari tergantung pada prinsipal.
Tip
Tidak perlu membuat navigasi virtual kecuali digunakan oleh proksi pemuatan malas atau pelacakan perubahan.
Navigasi referensi
Navigasi datang dalam dua formulir --referensi dan koleksi. Navigasi referensi adalah referensi objek sederhana ke entitas lain. Mereka mewakili sisi "satu" dari hubungan satu-ke-banyak dan satu-ke-satu . Misalnya:
public Blog TheBlog { get; set; }
Navigasi referensi harus memiliki setter, meskipun tidak perlu publik. Navigasi referensi tidak boleh diinisialisasi secara otomatis ke nilai default non-null; melakukannya setara dengan menegaskan bahwa entitas ada ketika tidak.
Saat menggunakan jenis referensi C# nullable, navigasi referensi harus dapat diubah ke null untuk hubungan opsional:
public Blog? TheBlog { get; set; }
Navigasi referensi untuk hubungan yang diperlukan dapat nullable atau tidak dapat diubah ke null.
Navigasi koleksi
Navigasi koleksi adalah instans jenis koleksi .NET; yaitu, jenis apa pun yang mengimplementasikan ICollection<T>. Koleksi berisi instans jenis entitas terkait, yang dapat berupa angka apa pun. Mereka mewakili sisi "banyak" dari hubungan satu-ke-banyak dan banyak-ke-banyak . Misalnya:
public ICollection<Post> ThePosts { get; set; }
Navigasi koleksi tidak perlu memiliki setter. Adalah umum untuk menginisialisasi koleksi sebaris, sehingga menghapus kebutuhan untuk pernah memeriksa apakah properti adalah null
. Misalnya:
public ICollection<Post> ThePosts { get; } = new List<Post>();
Tip
Jangan secara tidak sengaja membuat properti bertubuh ekspresi, seperti public ICollection<Post> ThePosts => new List<Post>();
. Ini akan membuat instans koleksi baru yang kosong setiap kali properti diakses, dan oleh karena itu tidak akan berguna sebagai navigasi.
Tipe koleksi
Instans pengumpulan yang mendasar harus menerapkan ICollection<T>, dan harus memiliki metode kerja Add
. Adalah umum untuk menggunakan List<T> atau HashSet<T>. List<T>
efisien untuk sejumlah kecil entitas terkait dan mempertahankan pemesanan yang stabil. HashSet<T>
memiliki pencarian yang lebih efisien untuk sejumlah besar entitas, tetapi tidak memiliki urutan yang stabil. Anda juga dapat menggunakan implementasi koleksi kustom Anda sendiri.
Penting
Koleksi harus menggunakan kesetaraan referensi. Saat membuat HashSet<T>
untuk navigasi koleksi, pastikan untuk menggunakan ReferenceEqualityComparer.
Array tidak dapat digunakan untuk navigasi pengumpulan karena, meskipun menerapkan ICollection<T>
, Add
metode melemparkan pengecualian saat dipanggil.
Meskipun instans koleksi harus berupa ICollection<T>
, koleksi tidak perlu diekspos seperti itu. Misalnya, umum untuk mengekspos navigasi sebagai IEnumerable<T>, yang menyediakan tampilan baca-saja yang tidak dapat dimodifikasi secara acak oleh kode aplikasi. Misalnya:
public class Blog
{
public int Id { get; set; }
public IEnumerable<Post> ThePosts { get; } = new List<Post>();
}
Variasi pada pola ini mencakup metode untuk manipulasi koleksi sesuai kebutuhan. Misalnya:
public class Blog
{
private readonly List<Post> _posts = new();
public int Id { get; set; }
public IEnumerable<Post> Posts => _posts;
public void AddPost(Post post) => _posts.Add(post);
}
Kode aplikasi masih dapat mentransmisikan koleksi yang diekspos ke dan ICollection<T>
kemudian memanipulasinya. Jika ini menjadi perhatian, maka entitas dapat mengembalikan salinan defensif koleksi. Misalnya:
public class Blog
{
private readonly List<Post> _posts = new();
public int Id { get; set; }
public IEnumerable<Post> Posts => _posts.ToList();
public void AddPost(Post post) => _posts.Add(post);
}
Pertimbangkan dengan cermat apakah nilai yang diperoleh dari ini cukup tinggi sehingga melebihi overhead pembuatan salinan koleksi setiap kali navigasi diakses.
Tip
Pola akhir ini berfungsi karena, secara default, EF mengakses koleksi melalui bidang dukungannya. Ini berarti bahwa EF itu sendiri menambahkan dan menghapus entitas dari koleksi aktual, sementara aplikasi hanya berinteraksi dengan salinan defensif koleksi.
Inisialisasi navigasi koleksi
Navigasi koleksi dapat diinisialisasi oleh jenis entitas, baik dengan bersemangat:
public class Blog
{
public ICollection<Post> Posts { get; } = new List<Post>();
}
Atau malas:
public class Blog
{
private ICollection<Post>? _posts;
public ICollection<Post> Posts => _posts ??= new List<Post>();
}
Jika EF perlu menambahkan entitas ke navigasi koleksi, misalnya, saat menjalankan kueri, maka EF akan menginisialisasi koleksi jika saat ini null
. Instans yang dibuat tergantung pada jenis navigasi yang diekspos.
- Jika navigasi diekspos sebagai
HashSet<T>
, maka instans penggunaanHashSet<T>
ReferenceEqualityComparer dibuat. - Jika tidak, jika navigasi diekspos sebagai jenis beton dengan konstruktor tanpa parameter, maka instans jenis beton tersebut dibuat. Ini berlaku untuk
List<T>
, tetapi juga untuk jenis koleksi lain, termasuk jenis koleksi kustom. - Jika tidak, jika navigasi diekspos sebagai
IEnumerable<T>
, ,ICollection<T>
atauISet<T>
, maka instans penggunaanHashSet<T>
ReferenceEqualityComparer
dibuat. - Jika tidak, jika navigasi diekspos sebagai
IList<T>
, maka instansList<T>
dibuat. - Jika tidak, pengecualian ditampilkan.
Catatan
Jika entitas pemberitahuan, termasuk proksi pelacakan perubahan, sedang digunakan, maka ObservableCollection<T> dan ObservableHashSet<T> digunakan sebagai pengganti List<T>
dan HashSet<T>
.
Penting
Seperti yang dijelaskan dalam dokumentasi pelacakan perubahan, EF hanya melacak satu instans entitas apa pun dengan nilai kunci tertentu. Ini berarti bahwa koleksi yang digunakan sebagai navigasi harus menggunakan semantik kesetaraan referensi. Jenis entitas yang tidak mengambil alih kesetaraan objek akan mendapatkan ini secara default. Pastikan untuk menggunakan ReferenceEqualityComparer saat membuat HashSet<T>
untuk digunakan sebagai navigasi untuk memastikannya berfungsi untuk semua jenis entitas.
Mengonfigurasi navigasi
Navigasi disertakan dalam model sebagai bagian dari mengonfigurasi hubungan. Artinya, berdasarkan konvensi, atau menggunakan HasOne
, , HasMany
dll. dalam API bangunan model. Sebagian besar konfigurasi yang terkait dengan navigasi dilakukan dengan mengonfigurasi hubungan itu sendiri.
Namun, ada beberapa jenis konfigurasi yang khusus untuk properti navigasi itu sendiri, daripada menjadi bagian dari konfigurasi hubungan keseluruhan. Jenis konfigurasi ini dilakukan dengan Navigation
metode . Misalnya, untuk memaksa EF mengakses navigasi melalui propertinya, daripada menggunakan bidang backing:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Navigation(e => e.Posts)
.UsePropertyAccessMode(PropertyAccessMode.Property);
modelBuilder.Entity<Post>()
.Navigation(e => e.Blog)
.UsePropertyAccessMode(PropertyAccessMode.Property);
}
Catatan
Panggilan Navigation
tidak dapat digunakan untuk membuat properti navigasi. Ini hanya digunakan untuk mengonfigurasi properti navigasi yang sebelumnya telah dibuat dengan menentukan hubungan atau dari konvensi.
Navigasi yang diperlukan
Navigasi dari dependen ke prinsipal diperlukan jika hubungan diperlukan, yang pada gilirannya berarti bahwa properti kunci asing tidak dapat diubah ke null. Sebaliknya, navigasi bersifat opsional jika kunci asing dapat diubah ke null, dan karenanya hubungan bersifat opsional.
Navigasi referensi dari utama ke dependen berbeda. Dalam kebanyakan kasus, entitas utama selalu dapat ada tanpa entitas dependen apa pun. Artinya, hubungan yang diperlukan tidak menunjukkan bahwa akan selalu ada setidaknya satu entitas dependen. Tidak ada cara dalam model EF, dan juga tidak ada cara standar dalam database relasional, untuk memastikan bahwa prinsipal dikaitkan dengan sejumlah dependen tertentu. Jika ini diperlukan, maka harus diimplementasikan dalam logika aplikasi (bisnis).
Ada satu pengecualian untuk aturan ini--ketika jenis utama dan dependen berbagi tabel yang sama dalam database relasional, atau terkandung dalam dokumen. Ini dapat terjadi dengan jenis yang dimiliki, atau jenis yang tidak dimiliki yang berbagi tabel yang sama. Dalam hal ini, properti navigasi dari utama ke dependen dapat ditandai sebagaimana diperlukan, menunjukkan bahwa dependen harus ada.
Konfigurasi properti navigasi sesuai kebutuhan dilakukan menggunakan Navigation
metode . Contohnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Navigation(e => e.BlogHeader)
.IsRequired();
}