Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
EF Core menggunakan serangkaian konvensi saat menemukan dan membangun model berdasarkan kelas jenis entitas. Dokumen ini meringkas konvensi yang digunakan untuk menemukan dan mengonfigurasi hubungan antar jenis entitas.
Penting
Konvensi yang dijelaskan di sini dapat digantikan oleh konfigurasi hubungan secara eksplisit menggunakan atribut pemetaan atau API pembuatan model.
Petunjuk / Saran
Kode di bawah ini dapat ditemukan di RelationshipConventions.cs.
Menemukan navigasi
Penemuan hubungan dimulai dengan menemukan navigasi antar jenis entitas.
Navigasi referensi
Properti dari jenis entitas ditemukan sebagai navigasi referensi ketika:
- Properti ini bersifat publik.
- Properti ini memiliki getter dan setter.
- Setter tidak perlu bersifat terbuka; bisa bersifat pribadi atau memiliki aksesibilitas lainnya.
- Setter dapat berupa Init-only.
- Jenis properti adalah, atau dapat merupakan, jenis entitas. Ini berarti bahwa jenis
- Harus merupakan tipe referensi.
- Tidak boleh dikonfigurasi secara eksplisit sebagai jenis properti primitif.
- Tidak boleh dipetakan sebagai jenis properti primitif oleh penyedia database yang digunakan.
- Tidak boleh dikonversi secara otomatis ke jenis properti primitif yang dipetakan oleh penyedia database yang digunakan.
- Properti tidak statis.
- Properti bukan properti pengindeks.
Misalnya, pertimbangkan jenis entitas berikut:
public class Blog
{
// Not discovered as reference navigations:
public int Id { get; set; }
public string Title { get; set; } = null!;
public Uri? Uri { get; set; }
public ConsoleKeyInfo ConsoleKeyInfo { get; set; }
public Author DefaultAuthor => new() { Name = $"Author of the blog {Title}" };
// Discovered as a reference navigation:
public Author? Author { get; private set; }
}
public class Author
{
// Not discovered as reference navigations:
public Guid Id { get; set; }
public string Name { get; set; } = null!;
public int BlogId { get; set; }
// Discovered as a reference navigation:
public Blog Blog { get; init; } = null!;
}
Untuk jenis ini, Blog.Author dan Author.Blog ditemukan sebagai navigasi referensi. Di sisi lain, properti berikut tidak ditemukan sebagai navigasi referensi:
-
Blog.Id, karenaintadalah jenis primitif yang dipetakan -
Blog.Title, karena 'string' adalah jenis primitif yang dipetakan -
Blog.Uri, karenaUrisecara otomatis dikonversi ke jenis primitif yang dipetakan -
Blog.ConsoleKeyInfo, karenaConsoleKeyInfoadalah jenis nilai C# -
Blog.DefaultAuthor, karena properti tidak memiliki setter -
Author.Id, karenaGuidadalah jenis primitif yang dipetakan -
Author.Name, karena 'string' adalah jenis primitif yang dipetakan -
Author.BlogId, karenaintadalah jenis primitif yang dipetakan
Navigasi koleksi
Properti jenis entitas ditemukan sebagai navigasi koleksi saat:
- Properti ini bersifat publik.
- Properti ini memiliki getter. Navigasi koleksi dapat memiliki setter, tetapi ini tidak diperlukan.
- Jenis properti adalah atau mengimplementasikan
IEnumerable<TEntity>, di manaTEntityadalah, atau bisa merupakan, jenis entitas. Hal ini menandakan bahwa jenisTEntity:- Harus merupakan tipe referensi.
- Tidak boleh dikonfigurasi secara eksplisit sebagai jenis properti primitif.
- Tidak boleh dipetakan sebagai jenis properti primitif oleh penyedia database yang digunakan.
- Tidak boleh dikonversi secara otomatis ke jenis properti primitif yang dipetakan oleh penyedia database yang digunakan.
- Properti tidak statis.
- Properti bukan properti pengindeks.
Misalnya, dalam kode berikut, keduanya Blog.Tags dan Tag.Blogs ditemukan sebagai navigasi koleksi:
public class Blog
{
public int Id { get; set; }
public List<Tag> Tags { get; set; } = null!;
}
public class Tag
{
public Guid Id { get; set; }
public IEnumerable<Blog> Blogs { get; } = new List<Blog>();
}
Navigasi pemasangan pasangan
Setelah navigasi dari, misalnya, jenis entitas A ke jenis entitas B ditemukan, langkah berikutnya adalah menentukan apakah navigasi tersebut memiliki arah inversi yang berlawanan—yaitu, dari jenis entitas B ke jenis entitas A. Jika inversi seperti itu ditemukan, kedua navigasi tersebut dipasangkan untuk membentuk satu hubungan dua arah yang saling terhubung.
Jenis hubungan ditentukan berdasarkan apakah navigasi dan inversinya berupa navigasi referensi atau koleksi. Khususnya:
- Jika satu navigasi adalah navigasi koleksi dan navigasi lainnya adalah navigasi referensi, maka hubungannya adalah satu-ke-banyak.
- Jika kedua navigasi adalah navigasi referensi, maka hubungannya adalah satu-ke-satu.
- Jika kedua navigasi adalah navigasi koleksi, maka hubungannya banyak-ke-banyak.
Penemuan masing-masing jenis hubungan ini ditunjukkan dalam contoh di bawah ini:
Hubungan tunggal satu-ke-banyak antara Blog dan Post ditemukan melalui memasangkan navigasi Blog.Posts dan Post.Blog.
public class Blog
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public int? BlogId { get; set; }
public Blog? Blog { get; set; }
}
Hubungan satu-ke-satu ditemukan antara Blog dan Author melalui pemasangan navigasi Blog.Author dan Author.Blog.
public class Blog
{
public int Id { get; set; }
public Author? Author { get; set; }
}
public class Author
{
public int Id { get; set; }
public int? BlogId { get; set; }
public Blog? Blog { get; set; }
}
Hubungan tunggal ke banyak ditemukan antara Post dan Tag ditemukan dengan memasangkan Post.Tags navigasi dan Tag.Posts :
public class Post
{
public int Id { get; set; }
public ICollection<Tag> Tags { get; } = new List<Tag>();
}
public class Tag
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
Nota
Pasangan navigasi ini mungkin salah jika kedua navigasi mewakili dua hubungan searah yang berbeda. Dalam hal ini, kedua hubungan harus dikonfigurasi secara eksplisit.
Pemasangan hubungan hanya berfungsi ketika ada satu hubungan antara dua jenis. Beberapa hubungan antara dua jenis harus dikonfigurasi secara eksplisit.
Nota
Deskripsi di sini adalah dalam hal hubungan antara dua jenis yang berbeda. Namun, dimungkinkan bagi jenis yang sama untuk berada di kedua ujung hubungan, dan jenis yang sama dapat memiliki dua navigasi yang saling dipasangkan. Ini disebut hubungan referensi mandiri.
Menemukan properti kunci asing
Setelah navigasi untuk hubungan ditemukan atau dikonfigurasi secara eksplisit, navigasi ini digunakan untuk menemukan properti kunci asing yang sesuai untuk hubungan tersebut. Properti ditemukan sebagai kunci asing saat:
- Jenis properti kompatibel dengan kunci utama atau alternatif pada jenis entitas utama.
- Jenis dianggap kompatibel jika mereka sama, atau jika jenis properti kunci asing adalah versi yang dapat di-null-kan dari jenis properti kunci utama atau alternatif.
- Nama properti cocok dengan salah satu konvensi penamaan untuk properti kunci asing. Konvensi penamaannya adalah:
<navigation property name><principal key property name><navigation property name>Id<principal entity type name><principal key property name><principal entity type name>Id
- Selain itu, jika ujung dependen telah dikonfigurasi secara eksplisit menggunakan API untuk membangun model, dan kunci utama dependen tersebut kompatibel, maka kunci utama dependen juga akan digunakan sebagai kunci asing.
Petunjuk / Saran
Akhiran "Id" dapat memiliki casing apa pun.
Jenis entitas berikut menunjukkan contoh untuk masing-masing konvensi penamaan ini.
Post.TheBlogKey ditemukan sebagai kunci asing karena cocok dengan pola <navigation property name><principal key property name>:
public class Blog
{
public int Key { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public int? TheBlogKey { get; set; }
public Blog? TheBlog { get; set; }
}
Post.TheBlogID ditemukan sebagai kunci asing karena cocok dengan pola <navigation property name>Id:
public class Blog
{
public int Key { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public int? TheBlogID { get; set; }
public Blog? TheBlog { get; set; }
}
Post.BlogKey ditemukan sebagai kunci asing karena cocok dengan pola <principal entity type name><principal key property name>:
public class Blog
{
public int Key { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public int? BlogKey { get; set; }
public Blog? TheBlog { get; set; }
}
Post.Blogid ditemukan sebagai kunci asing karena cocok dengan pola <principal entity type name>Id:
public class Blog
{
public int Key { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public int? Blogid { get; set; }
public Blog? TheBlog { get; set; }
}
Nota
Dalam kasus navigasi satu-ke-banyak, properti kunci asing harus berada pada jenis dengan navigasi referensi, karena entitas ini akan menjadi entitas dependen. Dalam kasus hubungan satu-ke-satu, penemuan properti kunci asing digunakan untuk menentukan jenis mana yang mewakili akhir hubungan dependen. Jika tidak ada properti kunci asing yang ditemukan, maka akhir dependen harus dikonfigurasi menggunakan HasForeignKey. Lihat Hubungan satu-ke-satu untuk contoh ini.
Aturan di atas juga berlaku untuk komposit kunci asing, di mana setiap properti komposit harus memiliki jenis yang kompatibel dengan properti yang sesuai dari kunci utama atau alternatif, dan setiap nama properti harus cocok dengan salah satu konvensi penamaan yang dijelaskan di atas.
Menentukan kardinalitas
EF menggunakan navigasi yang ditemukan dan properti kunci asing untuk menentukan kardinalitas hubungan bersama dengan ujung utama dan dependennya:
- Jika ada navigasi referensi yang tidak berpasangan, maka hubungan dikonfigurasi sebagai satu-ke-banyak yang searah, dengan navigasi referensi pada akhir dependen.
- Jika ada navigasi koleksi yang tidak berpasangan, maka hubungan dikonfigurasi sebagai satu-ke-banyak unidirectional, dengan navigasi koleksi sebagai pihak utama.
- Jika ada navigasi referensi dan koleksi yang dipasangkan, maka hubungan dikonfigurasi sebagai dua arah satu ke banyak, dengan navigasi koleksi di ujung utama.
- Jika navigasi referensi dipasangkan dengan navigasi referensi lain, maka:
- Jika properti kunci asing ditemukan di satu sisi tetapi tidak yang lain, maka hubungan dikonfigurasi sebagai satu-ke-satu yang bersifat dua arah, dengan properti kunci asing berada di sisi dependen.
- Jika tidak, sisi dependen tidak dapat ditentukan dan EF memberikan pengecualian yang menunjukkan bahwa dependen harus dikonfigurasi secara eksplisit.
- Jika navigasi koleksi dipasangkan dengan navigasi koleksi lain, maka hubungan dikonfigurasi sebagai banyak-ke-banyak dua arah.
Properti kunci asing bayangan
Jika EF telah menentukan akhir hubungan dependen tetapi tidak ada properti kunci asing yang ditemukan, maka EF akan membuat properti bayangan untuk mewakili kunci asing. Properti bayangan
- Memiliki jenis properti kunci utama atau alternatif di sisi utama dari hubungan.
- Tipe ini dibuat nullable secara default, menjadikan hubungan opsional secara default.
- Jika terdapat navigasi pada ujung dependen, maka properti kunci asing bayangan dinamai menggunakan nama navigasi tersebut yang digabungkan dengan nama properti kunci utama atau alternatif.
- Jika tidak ada navigasi pada sisi dependen, maka properti kunci asing bayangan dinamai menggunakan nama jenis entitas utama yang digabungkan dengan nama properti kunci utama atau alternatif.
Penghapusan Kaskade
Menurut konvensi, hubungan yang diperlukan dikonfigurasi untuk penghapusan berurutan. Hubungan opsional dikonfigurasi agar tidak melakukan penghapusan berantai.
Banyak-ke-banyak
Hubungan banyak ke banyak tidak memiliki ujung utama dan dependen, dan tidak ada ujung yang berisi properti kunci asing. Sebaliknya, hubungan banyak ke banyak menggunakan jenis entitas penghubung yang berisi pasangan kunci asing yang menunjuk ke kedua ujung dari hubungan banyak ke banyak. Pertimbangkan tipe entitas berikut, yang mana hubungan banyak ke banyak teridentifikasi melalui konvensi.
public class Post
{
public int Id { get; set; }
public ICollection<Tag> Tags { get; } = new List<Tag>();
}
public class Tag
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
Konvensi yang digunakan dalam penemuan ini adalah:
- Jenis entitas gabungan diberi nama
<left entity type name><right entity type name>. Jadi,PostTagdalam contoh ini.- Tabel gabungan memiliki nama yang sama dengan jenis entitas gabungan.
- Jenis entitas gabungan diberi properti kunci asing untuk setiap arah hubungan. Ini diberi nama
<navigation name><principal key name>. Jadi, dalam contoh ini, properti kunci asing adalahPostsIddanTagsId.- Untuk hubungan banyak-ke-banyak searah, properti kunci asing tanpa navigasi terkait diberi nama
<principal entity type name><principal key name>.
- Untuk hubungan banyak-ke-banyak searah, properti kunci asing tanpa navigasi terkait diberi nama
- Properti kunci asing tidak dapat bernilai null, mengharuskan kedua hubungan dengan entitas gabungan.
- Konvensi penghapusan kaskade berarti bahwa hubungan ini akan dikonfigurasi untuk penghapusan kaskade.
- Jenis entitas gabungan dikonfigurasi dengan kunci primer komposit yang terdiri dari dua properti kunci asing. Jadi, dalam contoh ini, kunci primer terdiri dari
PostsIddanTagsId.
Ini menghasilkan model EF berikut:
Model:
EntityType: Post
Properties:
Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
Skip navigations:
Tags (ICollection<Tag>) CollectionTag Inverse: Posts
Keys:
Id PK
EntityType: Tag
Properties:
Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
Skip navigations:
Posts (ICollection<Post>) CollectionPost Inverse: Tags
Keys:
Id PK
EntityType: PostTag (Dictionary<string, object>) CLR Type: Dictionary<string, object>
Properties:
PostsId (no field, int) Indexer Required PK FK AfterSave:Throw
TagsId (no field, int) Indexer Required PK FK Index AfterSave:Throw
Keys:
PostsId, TagsId PK
Foreign keys:
PostTag (Dictionary<string, object>) {'PostsId'} -> Post {'Id'} Cascade
PostTag (Dictionary<string, object>) {'TagsId'} -> Tag {'Id'} Cascade
Indexes:
TagsId
Dan diterjemahkan ke skema database berikut saat menggunakan SQLite:
CREATE TABLE "Posts" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_Posts" PRIMARY KEY AUTOINCREMENT);
CREATE TABLE "Tag" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_Tag" PRIMARY KEY AUTOINCREMENT);
CREATE TABLE "PostTag" (
"PostsId" INTEGER NOT NULL,
"TagsId" INTEGER NOT NULL,
CONSTRAINT "PK_PostTag" PRIMARY KEY ("PostsId", "TagsId"),
CONSTRAINT "FK_PostTag_Posts_PostsId" FOREIGN KEY ("PostsId") REFERENCES "Posts" ("Id") ON DELETE CASCADE,
CONSTRAINT "FK_PostTag_Tag_TagsId" FOREIGN KEY ("TagsId") REFERENCES "Tag" ("Id") ON DELETE CASCADE);
CREATE INDEX "IX_PostTag_TagsId" ON "PostTag" ("TagsId");
Indeks
Menurut konvensi, EF membuat indeks database untuk properti atau properti kunci asing. Jenis indeks yang dibuat ditentukan oleh:
- Kardinalitas hubungan
- Apakah hubungan bersifat opsional atau diperlukan
- Jumlah properti yang membentuk kunci asing
Untuk hubungan satu ke banyak, indeks yang sederhana dibuat sesuai dengan konvensi. Indeks yang sama dibuat untuk hubungan yang opsional dan yang diperlukan. Misalnya, pada SQLite:
CREATE INDEX "IX_Post_BlogId" ON "Post" ("BlogId");
Atau di SQL Server:
CREATE INDEX [IX_Post_BlogId] ON [Post] ([BlogId]);
Untuk hubungan satu-ke-satu yang diperlukan, indeks unik dibuat. Misalnya, pada SQLite:
CREATE UNIQUE INDEX "IX_Author_BlogId" ON "Author" ("BlogId");
Atau di SQL Sever:
CREATE UNIQUE INDEX [IX_Author_BlogId] ON [Author] ([BlogId]);
Untuk hubungan opsional satu-ke-satu, indeks yang dibuat di SQLite sama:
CREATE UNIQUE INDEX "IX_Author_BlogId" ON "Author" ("BlogId");
Namun, di SQL Server, IS NOT NULL filter ditambahkan untuk menangani nilai kunci asing null dengan lebih baik. Contohnya:
CREATE UNIQUE INDEX [IX_Author_BlogId] ON [Author] ([BlogId]) WHERE [BlogId] IS NOT NULL;
Untuk kunci asing komposit, indeks dibuat yang mencakup semua kolom kunci asing. Contohnya:
CREATE INDEX "IX_Post_ContainingBlogId1_ContainingBlogId2" ON "Post" ("ContainingBlogId1", "ContainingBlogId2");
Nota
EF tidak membuat indeks untuk properti yang sudah dicakup oleh indeks yang ada atau batasan kunci utama.
Cara menghentikan pembuatan indeks EF untuk kunci asing
Indeks memiliki overhead, dan, seperti yang ditanyakan di sini, mungkin tidak selalu tepat untuk membuatnya untuk semua kolom FK. Untuk mencapai hal ini, ForeignKeyIndexConvention dapat dihapus saat membangun model:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
}
Jika diinginkan, indeks masih dapat dibuat secara eksplisit untuk kolom kunci asing yang memang membutuhkannya.
Nama batasan kunci asing
Berdasarkan konvensi batasan kunci asing diberi nama FK_<dependent type name>_<principal type name>_<foreign key property name>. Untuk kunci asing komposit, <foreign key property name> menjadi daftar nama properti kunci asing yang dipisahkan garis bawah.