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.
Nota
Fitur ini ditambahkan di EF Core 7.
Meskipun rekayasa terbalik, Entity Framework Core berusaha untuk menyusun kode tujuan umum yang baik dan dapat digunakan dalam berbagai jenis aplikasi dan menggunakan konvensi pengkodean umum untuk tampilan yang konsisten dan nuansa yang akrab. Namun, terkadang, kode yang lebih khusus dan gaya pengodean alternatif diinginkan. Artikel ini memperlihatkan cara mengkustomisasi kode perancah menggunakan templat teks T4.
Prasyarat
Artikel ini mengasumsikan Anda terbiasa dengan rekayasa terbalik di EF Core. Jika tidak, silakan tinjau artikel tersebut sebelum melanjutkan.
Menambahkan templat default
Langkah pertama untuk menyesuaikan kode yang dihasilkan adalah menambahkan templat default ke proyek Anda. Templat bawaan adalah templat yang digunakan secara internal oleh EF Core saat rekayasa mundur. Mereka menyediakan titik awal bagi Anda untuk mulai menyesuaikan kode yang sudah dipersiapkan.
Mulailah dengan menginstal paket templat EF Core untuk dotnet new
:
dotnet new install Microsoft.EntityFrameworkCore.Templates
Sekarang Anda dapat menambahkan templat default ke proyek Anda. Lakukan ini dengan menjalankan perintah berikut dari direktori proyek Anda.
dotnet new ef-templates
Perintah ini menambahkan file berikut ke proyek Anda.
- CodeTemplates/
- EFCore/
- DbContext.t4
- EntityType.t4
- EFCore/
Templat DbContext.t4
digunakan untuk membuat perancah kelas DbContext untuk database, dan templat EntityType.t4
digunakan untuk membuat perancah kelas jenis entitas untuk setiap tabel dan tampilan dalam database.
Petunjuk / Saran
Ekstensi .t4 digunakan (bukan .tt) untuk mencegah Visual Studio mengubah templat. Templat akan diubah oleh EF Core sebagai gantinya.
Pengantar T4
Mari kita buka templat DbContext.t4
dan periksa kontennya. File ini adalah templat teks T4. T4 adalah bahasa untuk menghasilkan teks menggunakan .NET. Kode berikut hanya untuk tujuan ilustrasi; ini tidak mewakili isi lengkap file.
Penting
Templat teks T4--terutama yang menghasilkan kode--bisa sulit dibaca tanpa penyorotan sintaks. Jika perlu, cari ekstensi ke editor kode Anda yang mengaktifkan penyorotan sintaks T4.
<#@ template hostSpecific="true" #>
<#@ assembly name="Microsoft.EntityFrameworkCore.Design" #>
<#@ parameter name="NamespaceHint" type="System.String" #>
<#@ import namespace="Microsoft.EntityFrameworkCore" #>
<#
if (!string.IsNullOrEmpty(NamespaceHint))
{
#>
namespace <#= NamespaceHint #>;
Beberapa baris pertama yang dimulai dengan <#@
disebut direktif. Mereka memengaruhi bagaimana templat diubah bentuknya. Tabel berikut ini secara singkat menjelaskan setiap jenis direktif yang digunakan.
Direktif | Deskripsi |
---|---|
template |
Menentukan hostSpecific="true" yang memungkinkan penggunaan properti Host di dalam templat untuk mengakses layanan EF Core. |
assembly |
Menambahkan referensi rakitan yang diperlukan untuk mengkompilasi templat. |
parameter |
Mendeklarasikan parameter yang akan diteruskan oleh EF Core saat mengubah templat. |
import |
Seperti menggunakan direktif C#, memasukkan namespace ke dalam cakupan untuk kode template. |
Setelah arahan, bagian berikutnya dari DbContext.t4
disebut blok kontrol. Blok kontrol standar dimulai dengan <#
dan diakhir dengan #>
. Kode di dalamnya akan dijalankan saat mengubah templat. Untuk daftar properti dan metode yang tersedia di dalam blok kontrol, lihat kelas TextTransformation.
Apa pun di luar blok kontrol akan disalin langsung ke output templat.
Blok kontrol ekspresi dimulai dengan <#=
. Kode di dalamnya akan dievaluasi dan hasilnya akan ditambahkan ke output templat. Ini mirip dengan argumen string terinterpolasi C#.
Untuk penjelasan yang lebih rinci dan lengkap tentang sintaks T4, lihat Menulis Templat Teks T4.
Mengkustomisasi jenis entitas
Mari kita telusuri bagaimana rasanya mengkustomisasi templat. Secara default, EF Core menghasilkan kode berikut untuk properti navigasi koleksi.
public virtual ICollection<Album> Albums { get; } = new List<Album>();
Menggunakan List<T>
adalah default yang baik untuk sebagian besar aplikasi. Namun, jika Anda menggunakan kerangka kerja berbasis XAML seperti WPF, WinUI, atau .NET MAUI, Anda sering ingin menggunakan ObservableCollection<T>
sebagai gantinya untuk mengaktifkan pengikatan data.
Buka templat EntityType.t4
dan temukan di mana templat tersebut menghasilkan List<T>
. Sepertinya ini:
if (navigation.IsCollection)
{
#>
public virtual ICollection<<#= targetType #>> <#= navigation.Name #> { get; } = new List<<#= targetType #>>();
<#
}
Ganti Daftar dengan ObservableCollection.
public virtual ICollection<<#= targetType #>> <#= navigation.Name #> { get; } = new ObservableCollection<<#= targetType #>>();
Kita juga harus menambahkan arahan using
ke dalam kode perancah. Penggunaan ditentukan dalam daftar di dekat bagian atas templat. Tambahkan System.Collections.ObjectModel
ke daftar.
var usings = new List<string>
{
"System",
"System.Collections.Generic",
"System.Collections.ObjectModel"
};
Uji perubahan dengan menggunakan perintah rekayasa terbalik. Templat di dalam proyek Anda digunakan secara otomatis oleh perintah.
dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer
Jika Anda telah menjalankan perintah sebelumnya, tambahkan opsi --force
tersebut untuk menimpa file-file yang sudah ada.
Jika Anda melakukan semua langkah dengan benar, properti navigasi koleksi sekarang harus menggunakan ObservableCollection<T>
.
public virtual ICollection<Album> Albums { get; } = new ObservableCollection<Album>();
Memperbarui templat
Saat Anda menambahkan templat default ke proyek Anda, templat tersebut akan membuat salinannya berdasarkan versi EF Core tersebut. Karena bug diperbaiki dan fitur ditambahkan dalam versi EF Core berikutnya, templat Anda mungkin kedaluarsa. Anda harus meninjau perubahan yang dibuat dalam templat EF Core dan menggabungkannya ke dalam templat yang disesuaikan.
Salah satu cara untuk meninjau perubahan yang dilakukan pada templat EF Core adalah dengan menggunakan git untuk membandingkannya di antara versi. Perintah berikut akan mengkloning repositori EF Core dan menghasilkan perbedaan file-file ini antara versi 7.0.0 dan 8.0.0.
git clone --no-checkout https://github.com/dotnet/efcore.git
cd efcore
git diff v7.0.0 v8.0.0 -- src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt src/EFCore.Design/Scaffolding/Internal/CSharpEntityTypeGenerator.tt
Cara lain untuk meninjau perubahan adalah dengan mengunduh dua versi Microsoft.EntityFrameworkCore.Templates dari NuGet, mengekstrak kontennya (Anda dapat mengubah ekstensi file menjadi .zip), dan membandingkan file tersebut.
Sebelum menambahkan templat default ke proyek baru, ingatlah untuk memperbarui ke paket templat EF Core terbaru.
dotnet new update
Penggunaan tingkat lanjut
Mengabaikan model input
Parameter Model
dan EntityType
mewakili salah satu cara pemetaan yang mungkin ke database. Anda dapat memilih untuk mengabaikan atau mengubah bagian model. Misalnya, nama navigasi yang kami berikan mungkin tidak ideal, dan Anda dapat menggantinya dengan nama Anda sendiri saat membuat perancah kode. Hal lain seperti nama batasan dan filter indeks hanya digunakan oleh Migrasi dan dapat dihilangkan dengan aman dari model jika Anda tidak berniat menggunakan Migrasi dengan kode perancah. Demikian juga, Anda mungkin ingin menghilangkan urutan atau batasan default jika tidak digunakan oleh aplikasi Anda.
Saat membuat perubahan tingkat lanjut seperti ini, pastikan model yang dihasilkan tetap kompatibel dengan database. Meninjau SQL yang dihasilkan oleh dbContext.Database.GenerateCreateScript()
adalah cara yang baik untuk memvalidasi ini.
Kelas konfigurasi entitas
Untuk model yang lebih besar, metode OnModelCreating dari kelas DbContext dapat menjadi sulit untuk dikelola karena ukurannya. Salah satu cara untuk mengatasinya adalah dengan menggunakan kelas IEntityTypeConfiguration<T>
. Lihat Membuat dan mengonfigurasi model untuk informasi selengkapnya tentang kelas-kelas ini.
Untuk menyusun kelas ini, Anda dapat menggunakan templat ketiga yang disebut EntityTypeConfiguration.t4
. Seperti templat EntityType.t4
, templat akan digunakan untuk setiap jenis entitas dalam model dan menggunakan parameter templat EntityType
.
Hasilkan Tabel Penghubung pada Hubungan Banyak ke Banyak
Secara bawaan, proses perancah pemrograman tidak menghasilkan entitas untuk tabel gabungan dalam hubungan banyak-ke-banyak sederhana. Namun, ada kasus di mana secara eksplisit menghasilkan tabel gabungan sebagai entitas mungkin diperlukan (misalnya, ketika kontrol yang lebih halus atas kueri SQL yang dihasilkan diperlukan).
Perilaku perancah untuk setiap entitas dikontrol oleh file templat EntityType.t4. Dalam file ini, terdapat kondisi yang menghentikan pembuatan entitas untuk tabel penghubung banyak-ke-banyak yang sederhana. Untuk mengambil alih perilaku ini dan menghasilkan entitas gabungan, Anda dapat mengomentari kondisi ini dalam file 'EntityType.t4'.
<#
// Comment this condition
if (EntityType.IsSimpleManyToManyJoinEntityType())
{
// Don't scaffold these
return "";
}
. . .
#>
Perancah jenis file lain
Tujuan utama rekayasa terbalik di EF Core adalah untuk menyusun DbContext dan jenis entitas. Namun, tidak ada dalam alat yang mengharuskan Anda untuk benar-benar membangun kerangka kode. Misalnya, Anda dapat menyusun diagram hubungan entitas menggunakan Mermaid.
<#@ output extension=".md" #>
<#@ assembly name="Microsoft.EntityFrameworkCore" #>
<#@ assembly name="Microsoft.EntityFrameworkCore.Relational" #>
<#@ assembly name="Microsoft.EntityFrameworkCore.Design" #>
<#@ parameter name="Model" type="Microsoft.EntityFrameworkCore.Metadata.IModel" #>
<#@ parameter name="Options" type="Microsoft.EntityFrameworkCore.Scaffolding.ModelCodeGenerationOptions" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="Microsoft.EntityFrameworkCore" #>
# <#= Options.ContextName #>
```mermaid
erDiagram
<#
foreach (var entityType in Model.GetEntityTypes().Where(e => !e.IsSimpleManyToManyJoinEntityType()))
{
#>
<#= entityType.Name #> {
}
<#
foreach (var foreignKey in entityType.GetForeignKeys())
{
#>
<#= entityType.Name #> <#= foreignKey.IsUnique ? "|" : "}" #>o--<#= foreignKey.IsRequired ? "|" : "o" #>| <#= foreignKey.PrincipalEntityType.Name #> : "<#= foreignKey.GetConstraintName() #>"
<#
}
foreach (var skipNavigation in entityType.GetSkipNavigations().Where(n => n.IsLeftNavigation()))
{
#>
<#= entityType.Name #> }o--o{ <#= skipNavigation.TargetEntityType.Name #> : <#= skipNavigation.JoinEntityType.Name #>
<#
}
}
#>
```