Jenis entitas dengan konstruktor
Dimungkinkan untuk menentukan konstruktor dengan parameter dan meminta EF Core memanggil konstruktor ini saat membuat instans entitas. Parameter konstruktor dapat terikat ke properti yang dipetakan, atau ke berbagai jenis layanan untuk memfasilitasi perilaku seperti pemuatan malas.
Catatan
Saat ini, semua pengikatan konstruktor adalah berdasarkan konvensi. Konfigurasi konstruktor tertentu yang akan digunakan direncanakan untuk rilis mendatang.
Pengikatan ke properti yang dipetakan
Pertimbangkan model Blog/Posting yang khas:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<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 DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Ketika EF Core membuat instans jenis ini, seperti untuk hasil kueri, pertama-tama akan memanggil konstruktor tanpa parameter default lalu mengatur setiap properti ke nilai dari database. Namun, jika EF Core menemukan konstruktor berparameter dengan nama parameter dan jenis yang cocok dengan properti yang dipetakan, maka itu akan memanggil konstruktor berparameter dengan nilai untuk properti tersebut dan tidak akan mengatur setiap properti secara eksplisit. Contohnya:
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Yang perlu diperhatikan:
- Tidak semua properti harus memiliki parameter konstruktor. Misalnya, properti Post.Content tidak diatur oleh parameter konstruktor apa pun, sehingga EF Core akan mengaturnya setelah memanggil konstruktor dengan cara normal.
- Jenis dan nama parameter harus cocok dengan jenis dan nama properti, kecuali properti tersebut dapat di-kasus Pascal sementara parameternya di-camel-cased.
- EF Core tidak dapat mengatur properti navigasi (seperti Blog atau Posting di atas) menggunakan konstruktor.
- Konstruktor dapat bersifat publik, privat, atau memiliki aksesibilitas lain. Namun, proksi pemuatan malas mengharuskan konstruktor dapat diakses dari kelas proksi yang mewarisi. Biasanya ini berarti menjadikannya publik atau terlindungi.
Properti baca-saja
Setelah properti diatur melalui konstruktor, masuk akal untuk membuat beberapa dari properti tersebut baca-saja. EF Core mendukung hal ini, tetapi ada beberapa hal yang perlu diperhatikan:
- Properti tanpa setter tidak dipetakan oleh konvensi. (Melakukannya cenderung memetakan properti yang tidak boleh dipetakan, seperti properti komputasi.)
- Menggunakan nilai kunci yang dihasilkan secara otomatis memerlukan properti kunci yang dibaca-tulis, karena nilai kunci perlu diatur oleh generator kunci saat memasukkan entitas baru.
Cara mudah untuk menghindari hal-hal ini adalah dengan menggunakan setter privat. Contohnya:
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
public int Id { get; private set; }
public string Name { get; private set; }
public string Author { get; private set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}
public int Id { get; private set; }
public string Title { get; private set; }
public string Content { get; set; }
public DateTime PostedOn { get; private set; }
public Blog Blog { get; set; }
}
EF Core melihat properti dengan setter privat sebagai baca-tulis, yang berarti bahwa semua properti dipetakan seperti sebelumnya dan kunci masih dapat dihasilkan penyimpanan.
Alternatif untuk menggunakan setter privat adalah membuat properti benar-benar baca-saja dan menambahkan pemetaan yang lebih eksplisit di OnModelCreating. Demikian juga, beberapa properti dapat dihapus sepenuhnya dan diganti hanya dengan bidang. Misalnya, pertimbangkan jenis entitas ini:
public class Blog
{
private int _id;
public Blog(string name, string author)
{
Name = name;
Author = author;
}
public string Name { get; }
public string Author { get; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
private int _id;
public Post(string title, DateTime postedOn)
{
Title = title;
PostedOn = postedOn;
}
public string Title { get; }
public string Content { get; set; }
public DateTime PostedOn { get; }
public Blog Blog { get; set; }
}
Dan konfigurasi ini di OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>(
b =>
{
b.HasKey("_id");
b.Property(e => e.Author);
b.Property(e => e.Name);
});
modelBuilder.Entity<Post>(
b =>
{
b.HasKey("_id");
b.Property(e => e.Title);
b.Property(e => e.PostedOn);
});
}
Hal-hal yang harus diperhatikan:
- Kunci "properti" sekarang menjadi bidang. Ini bukan
readonly
bidang sehingga kunci yang dihasilkan penyimpanan dapat digunakan. - Properti lainnya adalah properti baca-saja yang diatur hanya di konstruktor.
- Jika nilai kunci utama hanya pernah ditetapkan oleh EF atau dibaca dari database, maka tidak perlu menyertakannya dalam konstruktor. Ini meninggalkan kunci "properti" sebagai bidang sederhana dan membuatnya jelas bahwa itu tidak boleh diatur secara eksplisit saat membuat blog atau posting baru.
Catatan
Kode ini akan menghasilkan peringatan kompilator '169' yang menunjukkan bahwa bidang tidak pernah digunakan. Ini dapat diabaikan karena pada kenyataannya EF Core menggunakan bidang dengan cara ekstralinguistik.
Menyuntikkan layanan
EF Core juga dapat menyuntikkan "layanan" ke dalam konstruktor jenis entitas. Misalnya, berikut ini dapat disuntikkan:
DbContext
- instans konteks saat ini, yang juga dapat ditik sebagai jenis DbContext turunan AndaILazyLoader
- layanan pemuatan malas--lihat dokumentasi pemuatan malas untuk detail selengkapnyaAction<object, string>
- delegasi pemuatan malas--lihat dokumentasi pemuatan malas untuk detail selengkapnyaIEntityType
- metadata EF Core yang terkait dengan jenis entitas ini
Catatan
Saat ini, hanya layanan yang diketahui oleh EF Core yang dapat disuntikkan. Dukungan untuk menyuntikkan layanan aplikasi sedang dipertimbangkan untuk rilis mendatang.
Misalnya, DbContext yang disuntikkan dapat digunakan untuk secara selektif mengakses database untuk mendapatkan informasi tentang entitas terkait tanpa memuat semuanya. Dalam contoh di bawah ini digunakan untuk mendapatkan jumlah posting dalam blog tanpa memuat posting:
public class Blog
{
public Blog()
{
}
private Blog(BloggingContext context)
{
Context = context;
}
private BloggingContext Context { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; set; }
public int PostsCount
=> Posts?.Count
?? Context?.Set<Post>().Count(p => Id == EF.Property<int?>(p, "BlogId"))
?? 0;
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Beberapa hal yang perlu diperhatikan tentang hal ini:
- Konstruktor bersifat pribadi, karena hanya pernah dipanggil oleh EF Core, dan ada konstruktor publik lain untuk penggunaan umum.
- Kode yang menggunakan layanan yang disuntikkan (yaitu, konteksnya) bersifat defensif terhadapnya untuk
null
menangani kasus di mana EF Core tidak membuat instans. - Karena layanan disimpan dalam properti baca/tulis, layanan akan diatur ulang saat entitas dilampirkan ke instans konteks baru.
Peringatan
Menyuntikkan DbContext seperti ini sering dianggap sebagai anti-pola karena menggandeng jenis entitas Anda langsung ke EF Core. Pertimbangkan dengan cermat semua opsi sebelum menggunakan injeksi layanan seperti ini.