Bagikan melalui


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 penggunaan HashSet<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>atau ISet<T>, maka instans penggunaan HashSet<T>ReferenceEqualityComparer dibuat.
  • Jika tidak, jika navigasi diekspos sebagai IList<T>, maka instans List<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, , HasManydll. 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();
}