Perancah (Rekayasa Terbalik)

Rekayasa terbalik adalah proses perancah kelas jenis entitas dan DbContext kelas berdasarkan skema database. Hal ini dapat dilakukan menggunakan Scaffold-DbContextperintah alat EF Core Package Manager Console (PMC) atau dotnet ef dbcontext scaffold perintah alat .NET Command-line Interface (CLI).

Catatan

Perancah jenis entitas dan yang DbContext didokumentasikan di sini berbeda dari perancah pengontrol di ASP.NET Core menggunakan Visual Studio, yang tidak didokumentasikan di sini.

Tip

Jika Anda menggunakan Visual Studio, cobalah ekstensi komunitas EF Core Power Tools . Alat-alat ini menyediakan alat grafis yang dibangun di atas alat baris perintah EF Core dan menawarkan alur kerja tambahan dan opsi kustomisasi.

Prasyarat

  • Sebelum perancah, Anda harus menginstal alat PMC, yang hanya berfungsi pada Visual Studio, atau alat .NET CLI, yang di semua platform yang didukung oleh .NET.
  • Instal paket NuGet untuk Microsoft.EntityFrameworkCore.Design pada proyek tempat Anda membuat perancah.
  • Instal paket NuGet untuk penyedia database yang menargetkan skema database yang ingin Anda perancah.

Argumen yang diperlukan

Perintah PMC dan .NET CLI memiliki dua argumen yang diperlukan: string koneksi ke database, dan penyedia database EF Core untuk digunakan.

String koneksi

Argumen pertama untuk perintah merupakan string koneksi ke database. Alat akan menggunakan string koneksi ini guna membaca skema database.

Cara Anda mengutip dan menghindari string koneksi tergantung pada shell mana yang Anda gunakan untuk menjalankan perintah; lihat dokumentasi shell Anda untuk informasi lebih lanjut. Misalnya, PowerShell mengharuskan Anda untuk keluar dari karakter $, tetapi tidak \.

Contoh berikut membuat perancah jenis entitas dan dari Chinook database yang DbContext terletak di instans SQL Server LocalDB komputer, yang menggunakan Microsoft.EntityFrameworkCore.SqlServer penyedia database.

dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer

Rahasia pengguna untuk string koneksi

Jika Anda memiliki aplikasi .NET yang menggunakan model hosting dan sistem konfigurasi, seperti proyek ASP.NET Core, maka Anda bisa menggunakan sintaksis Name=<connection-string> untuk membaca string koneksi dari konfigurasi.

Misalnya, pertimbangkan aplikasi ASP.NET Core dengan file konfigurasi berikut:

{
  "ConnectionStrings": {
    "Chinook": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=Chinook"
  }
}

Ini string koneksi dalam file konfigurasi dapat digunakan untuk perancah dari database menggunakan:

dotnet ef dbcontext scaffold "Name=ConnectionStrings:Chinook" Microsoft.EntityFrameworkCore.SqlServer

Namun, menyimpan string koneksi dalam file konfigurasi bukanlah ide yang baik, karena terlalu mudah untuk secara tidak sengaja mengeksposnya, misalnya, dengan mendorong ke kontrol sumber. Sebagai gantinya, string koneksi harus disimpan dengan cara yang aman, seperti menggunakan Azure Key Vault atau, saat bekerja secara lokal, alat Secret Manager, alias "Rahasia Pengguna".

Misalnya, untuk menggunakan Rahasia Pengguna, pertama-tama hapus string koneksi dari file konfigurasi ASP.NET Core Anda. Selanjutnya, inisialisasi Rahasia Pengguna dengan menjalankan perintah berikut di direktori yang sama dengan proyek ASP.NET Core:

dotnet user-secrets init

Perintah ini menyiapkan penyimpanan di komputer Anda terpisah dari kode sumber Anda dan menambahkan kunci untuk penyimpanan ini ke proyek.

Selanjutnya, simpan string koneksi dalam rahasia pengguna. Contohnya:

dotnet user-secrets set ConnectionStrings:Chinook "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook"

Sekarang perintah yang sama yang sebelumnya menggunakan string koneksi bernama dari file konfigurasi akan menggunakan string koneksi yang disimpan dalam Rahasia Pengguna. Contohnya:

dotnet ef dbcontext scaffold "Name=ConnectionStrings:Chinook" Microsoft.EntityFrameworkCore.SqlServer

untai (karakter) Koneksi dalam kode perancah

Secara default, perancah akan menyertakan string koneksi dalam kode perancah, tetapi dengan peringatan. Contohnya:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
    => optionsBuilder.UseSqlServer("Data Source=(LocalDb)\\MSSQLLocalDB;Database=AllTogetherNow");

Ini dilakukan agar kode yang dihasilkan tidak crash ketika pertama kali digunakan, yang akan menjadi pengalaman pembelajaran yang sangat buruk. Namun, seperti yang dikatakan peringatan, string koneksi seharusnya tidak ada dalam kode produksi. Lihat DbContext Lifetime, Configuration, and Initialization untuk berbagai cara yang dapat dikelola string koneksi.

Tip

Opsi -NoOnConfiguring (Visual Studio PMC) atau --no-onconfiguring (.NET CLI) dapat diteruskan untuk menekan pembuatan OnConfiguring metode yang berisi string koneksi.

Nama penyedia

Argumen kedua merupakan nama penyedia. Nama penyedia pada umumnya sama dengan nama paket NuGet penyedia. Misalnya, untuk SQL Server atau Azure SQL, gunakan Microsoft.EntityFrameworkCore.SqlServer.

Opsi baris Perintah

Proses perancah dapat dikontrol oleh berbagai opsi baris perintah.

Menentukan tabel dan tampilan

Secara default, semua tabel dan tampilan dalam skema database di-scaffold ke dalam jenis entitas. Anda dapat membatasi tabel dan tampilan mana yang di-scaffold dengan menentukan skema dan tabel.

Argumen -Schemas (Visual Studio PMC) atau --schema (.NET CLI) menentukan skema tabel dan tampilan untuk jenis entitas mana yang akan dihasilkan. Jika argumen ini dihilangkan, maka semua skema disertakan. Jika opsi ini digunakan, maka semua tabel dan tampilan dalam skema akan disertakan dalam model, bahkan jika mereka tidak secara eksplisit disertakan menggunakan -Tables atau --table.

Argumen -Tables (Visual Studio PMC) atau --table (.NET CLI) menentukan tabel dan tampilan untuk jenis entitas mana yang akan dihasilkan. Tabel atau tampilan dalam skema tertentu dapat disertakan menggunakan format 'schema.table' atau 'schema.view'. Jika opsi ini dihilangkan, maka semua tabel dan tampilan disertakan. |

Misalnya, untuk membuat perancah hanya Artists tabel dan Albums :

dotnet ef dbcontext scaffold ... --table Artist --table Album

Untuk membuat perancah semua tabel dan tampilan dari Customer skema dan Contractor :

dotnet ef dbcontext scaffold ... --schema Customer --schema Contractor

Misalnya, untuk membuat perancah Purchases tabel dari Customer skema, dan Accounts tabel dan Contracts dari Contractor skema:

dotnet ef dbcontext scaffold ... --table Customer.Purchases --table Contractor.Accounts --table Contractor.Contracts

Mempertahankan nama database

Nama tabel serta kolom diperbaiki agar lebih cocok dengan konvensi penamaan .NET untuk jenis dan properti secara default. -UseDatabaseNames Menentukan (Visual Studio PMC) atau --use-database-names (.NET CLI) akan menonaktifkan perilaku ini mempertahankan nama database asli sebanyak mungkin. Pengidentifikasi .NET yang tidak valid masih akan diperbaiki serta nama yang disintesis seperti properti navigasi masih akan sesuai dengan konvensi penamaan .NET.

Misalnya, pertimbangkan tabel berikut:

CREATE TABLE [BLOGS] (
    [ID] int NOT NULL IDENTITY,
    [Blog_Name] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Blogs] PRIMARY KEY ([ID]));

CREATE TABLE [posts] (
    [id] int NOT NULL IDENTITY,
    [postTitle] nvarchar(max) NOT NULL,
    [post content] nvarchar(max) NOT NULL,
    [1 PublishedON] datetime2 NOT NULL,
    [2 DeletedON] datetime2 NULL,
    [BlogID] int NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([id]),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogID]) REFERENCES [Blogs] ([ID]) ON DELETE CASCADE);

Secara default, jenis entitas berikut akan di-scaffold dari tabel ini:

public partial class Blog
{
    public int Id { get; set; }
    public string BlogName { get; set; } = null!;
    public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
}

public partial class Post
{
    public int Id { get; set; }
    public string PostTitle { get; set; } = null!;
    public string PostContent { get; set; } = null!;
    public DateTime _1PublishedOn { get; set; }
    public DateTime? _2DeletedOn { get; set; }
    public int BlogId { get; set; }
    public virtual Blog Blog { get; set; } = null!;
    public virtual ICollection<Tag> Tags { get; set; } = new List<Tag>();
}

Namun, menggunakan -UseDatabaseNames atau --use-database-names menghasilkan jenis entitas berikut:

public partial class BLOG
{
    public int ID { get; set; }
    public string Blog_Name { get; set; } = null!;
    public virtual ICollection<post> posts { get; set; } = new List<post>();
}

public partial class post
{
    public int id { get; set; }
    public string postTitle { get; set; } = null!;
    public string post_content { get; set; } = null!;
    public DateTime _1_PublishedON { get; set; }
    public DateTime? _2_DeletedON { get; set; }
    public int BlogID { get; set; }
    public virtual BLOG Blog { get; set; } = null!;
}

Menggunakan atribut pemetaan (alias Anotasi Data)

Jenis entitas dikonfigurasi menggunakan ModelBuilder API secara OnModelCreating default. Tentukan -DataAnnotations (PMC) atau --data-annotations (.NET Core CLI) untuk menggunakan atribut pemetaan jika memungkinkan.

Misalnya, penggunaan API Fasih akan membuat perancah ini:

entity.Property(e => e.Title)
    .IsRequired()
    .HasMaxLength(160);

Ketika menggunakan Anotasi Data akan membuat perancah ini:

[Required]
[StringLength(160)]
public string Title { get; set; }

Tip

Beberapa aspek model tidak dapat dikonfigurasi menggunakan atribut pemetaan. Perancah masih akan menggunakan API pembuatan model untuk menangani kasus-kasus ini.

Nama DbContext

Nama kelas perancah DbContext akan menjadi nama database yang diakhiri dengan Konteks secara default. Untuk menentukan yang berbeda, gunakan -Context dalam PMC dan --context di CLI .NET Core.

Direktori dan namespace target

Kelas entitas serta kelas DbContext diperancah ke direktori akar proyek serta menggunakan namespace default proyek.

Anda dapat menentukan direktori tempat kelas di-scaffold menggunakan --output-dir, dan --context-dir dapat digunakan untuk menyusun kelas DbContext ke direktori terpisah dari kelas jenis entitas:

dotnet ef dbcontext scaffold ... --context-dir Data --output-dir Models

Secara default, namespace layanan akan menjadi namespace layanan akar plus nama subdirektori apa pun yang berada di bawah direktori akar proyek. Namun, Anda dapat mengambil alih namespace untuk semua kelas output dengan menggunakan --namespace. Anda juga dapat mengganti namespace hanya untuk kelas DbContext menggunakan --context-namespace:

dotnet ef dbcontext scaffold ... --namespace Your.Namespace --context-namespace Your.DbContext.Namespace

Kode perancah

Hasil perancah dari database yang ada adalah:

  • File yang berisi kelas yang mewarisi dari DbContext
  • File untuk setiap jenis entitas

Tip

Mulai EF7, Anda juga bisa menggunakan template teks T4 untuk menyesuaikan kode yang dihasilkan. Lihat Template Rekayasa Terbalik Kustom untuk detail selengkapnya.

Jenis referensi C# Nullable

Perancah dapat membuat model EF dan jenis entitas yang menggunakan jenis referensi nullable C# (NRTs). Penggunaan NRT di-scaffold secara otomatis ketika dukungan NRT diaktifkan dalam proyek C# tempat kode sedang dibuat perancahnya.

Misalnya, tabel berikut berisi Tags kedua kolom string yang tidak dapat diubah ke null:

CREATE TABLE [Tags] (
  [Id] int NOT NULL IDENTITY,
  [Name] nvarchar(max) NOT NULL,
  [Description] nvarchar(max) NULL,
  CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));

Ini menghasilkan properti string nullable dan non-nullable yang sesuai di kelas yang dihasilkan:

public partial class Tag
{
    public Tag()
    {
        Posts = new HashSet<Post>();
    }

    public int Id { get; set; }
    public string Name { get; set; } = null!;
    public string? Description { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

Demikian pula, tabel berikut berisi Posts hubungan yang diperlukan ke Blogs tabel:

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Title] nvarchar(max) NOT NULL,
    [Contents] nvarchar(max) NOT NULL,
    [PostedOn] datetime2 NOT NULL,
    [UpdatedOn] datetime2 NULL,
    [BlogId] int NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]));

Ini menghasilkan perancah hubungan yang tidak dapat diubah ke null (diperlukan) antara blog:

public partial class Blog
{
    public Blog()
    {
        Posts = new HashSet<Post>();
    }

    public int Id { get; set; }
    public string Name { get; set; } = null!;

    public virtual ICollection<Post> Posts { get; set; }
}

Dan posting:

public partial class Post
{
    public Post()
    {
        Tags = new HashSet<Tag>();
    }

    public int Id { get; set; }
    public string Title { get; set; } = null!;
    public string Contents { get; set; } = null!;
    public DateTime PostedOn { get; set; }
    public DateTime? UpdatedOn { get; set; }
    public int BlogId { get; set; }

    public virtual Blog Blog { get; set; } = null!;

    public virtual ICollection<Tag> Tags { get; set; }
}

Hubungan banyak ke banyak

Proses perancah mendeteksi tabel gabungan sederhana dan secara otomatis menghasilkan pemetaan banyak ke banyak untuk mereka. Misalnya, pertimbangkan tabel untuk Posts dan Tags, dan tabel PostTag gabungan yang menghubungkannya:

CREATE TABLE [Tags] (
  [Id] int NOT NULL IDENTITY,
  [Name] nvarchar(max) NOT NULL,
  [Description] nvarchar(max) NULL,
  CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Title] nvarchar(max) NOT NULL,
    [Contents] nvarchar(max) NOT NULL,
    [PostedOn] datetime2 NOT NULL,
    [UpdatedOn] datetime2 NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]));

CREATE TABLE [PostTag] (
    [PostsId] int NOT NULL,
    [TagsId] int NOT NULL,
    CONSTRAINT [PK_PostTag] PRIMARY KEY ([PostsId], [TagsId]),
    CONSTRAINT [FK_PostTag_Posts_TagsId] FOREIGN KEY ([TagsId]) REFERENCES [Tags] ([Id]) ON DELETE CASCADE,
    CONSTRAINT [FK_PostTag_Tags_PostsId] FOREIGN KEY ([PostsId]) REFERENCES [Posts] ([Id]) ON DELETE CASCADE);

Saat di-scaffold, ini menghasilkan kelas untuk Post:

public partial class Post
{
    public Post()
    {
        Tags = new HashSet<Tag>();
    }

    public int Id { get; set; }
    public string Title { get; set; } = null!;
    public string Contents { get; set; } = null!;
    public DateTime PostedOn { get; set; }
    public DateTime? UpdatedOn { get; set; }
    public int BlogId { get; set; }

    public virtual Blog Blog { get; set; } = null!;

    public virtual ICollection<Tag> Tags { get; set; }
}

Dan kelas untuk Tag:

public partial class Tag
{
    public Tag()
    {
        Posts = new HashSet<Post>();
    }

    public int Id { get; set; }
    public string Name { get; set; } = null!;
    public string? Description { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

Tapi tidak ada kelas untuk PostTag meja. Sebagai gantinya, konfigurasi untuk hubungan banyak ke banyak di-scaffold:

entity.HasMany(d => d.Tags)
    .WithMany(p => p.Posts)
    .UsingEntity<Dictionary<string, object>>(
        "PostTag",
        l => l.HasOne<Tag>().WithMany().HasForeignKey("PostsId"),
        r => r.HasOne<Post>().WithMany().HasForeignKey("TagsId"),
        j =>
            {
                j.HasKey("PostsId", "TagsId");
                j.ToTable("PostTag");
                j.HasIndex(new[] { "TagsId" }, "IX_PostTag_TagsId");
            });

Bahasa pemrograman lainnya

Paket EF Core yang diterbitkan oleh kode C# perancah Microsoft. Namun, sistem perancah yang mendasar mendukung model plugin untuk perancah ke bahasa lain. Model plugin ini digunakan oleh berbagai proyek yang dijalankan komunitas, misalnya:

Menyesuaikan kode

Dimulai dengan EF7, salah satu cara terbaik untuk menyesuaikan kode yang dihasilkan adalah dengan menyesuaikan templat T4 yang digunakan untuk menghasilkannya.

Kode juga dapat diubah setelah dibuat, tetapi cara terbaik untuk melakukan ini tergantung pada apakah Anda ingin menjalankan kembali proses perancah ketika model database berubah.

Perancah sekali saja

Dengan pendekatan ini, kode perancah menyediakan titik awal untuk pemetaan berbasis kode ke depannya. Setiap perubahan pada kode yang dihasilkan dapat dibuat sesuai keinginan - itu menjadi kode normal seperti kode lain dalam proyek Anda.

Menjaga database dan model EF tetap sinkron dapat dilakukan dengan salah satu dari dua cara:

  • Beralih menggunakan migrasi database EF Core, dan gunakan jenis entitas dan konfigurasi model EF sebagai sumber kebenaran, menggunakan migrasi untuk mendorong skema.
  • Perbarui jenis entitas dan konfigurasi EF secara manual saat database berubah. Misalnya, jika kolom baru ditambahkan ke tabel, tambahkan properti untuk kolom ke jenis entitas yang dipetakan, dan tambahkan konfigurasi yang diperlukan menggunakan atribut pemetaan dan/atau kode di OnModelCreating. Ini relatif mudah, dengan satu-satunya tantangan nyata adalah proses untuk memastikan bahwa perubahan database dicatat atau terdeteksi dalam beberapa cara sehingga pengembang yang bertanggung jawab atas kode dapat bereaksi.

Perancah berulang

Pendekatan alternatif untuk perancah sekali adalah dengan perancah ulang setiap kali database berubah. Ini akan menimpa kode yang sebelumnya dibuat perancah, yang berarti setiap perubahan yang dilakukan pada jenis entitas atau konfigurasi EF dalam kode tersebut akan hilang.

[TIP] Secara default, perintah EF tidak akan menimpa kode yang ada untuk melindungi dari kehilangan kode yang tidak disengaja. Argumen -Force (Visual Studio PMC) atau --force (.NET CLI) dapat digunakan untuk memaksa penimpaan file yang ada.

Karena kode perancah akan ditimpa, yang terbaik adalah tidak memodifikasinya secara langsung, tetapi sebaliknya mengandalkan kelas dan metode parsial, dan mekanisme di EF Core yang memungkinkan konfigurasi ditimpa. Khususnya:

  • DbContext Kelas dan kelas entitas dihasilkan sebagai parsial. Ini memungkinkan pengenalan anggota dan kode tambahan dalam file terpisah yang tidak akan ditimpa saat perancah dijalankan.
  • Kelas DbContext berisi metode parsial yang disebut OnModelCreatingPartial. Implementasi metode ini dapat ditambahkan ke kelas parsial untuk DbContext. Kemudian akan dipanggil setelah OnModelCreating dipanggil.
  • Konfigurasi model yang ModelBuilder dibuat menggunakan API mengambil alih konfigurasi apa pun yang dilakukan oleh konvensi atau atribut pemetaan, serta konfigurasi sebelumnya yang dilakukan pada pembuat model. Ini berarti bahwa kode di OnModelCreatingPartial dapat digunakan untuk mengambil alih konfigurasi yang dihasilkan oleh proses perancah, tanpa perlu menghapus konfigurasi tersebut.

Terakhir, ingatlah bahwa dimulai dengan EF7, templat T4 yang digunakan untuk menghasilkan kode dapat disesuaikan. Ini seringkali merupakan pendekatan yang lebih efektif daripada perancah dengan default dan kemudian memodifikasi dengan kelas dan/atau metode parsial.

Cara kerjanya

Rekayasa terbalik dimulai dengan pembacaan skema database. Hal ini membaca informasi tentang tabel, kolom, batasan, serta indeks.

Selanjutnya, ia menggunakan informasi skema untuk membuat model EF Core. Tabel akan digunakan untuk membuat jenis entitas; kolom digunakan untuk membuat properti; serta kunci asing digunakan untuk membuat hubungan.

Akhirnya, model akan digunakan untuk menghasilkan kode. Kelas jenis entitas yang sesuai, API Fasih, dan anotasi data akan dibuat untuk membuat ulang model yang sama dari aplikasi Anda.

Batasan

  • Tidak semua hal tentang model dapat diwakili menggunakan skema database. Misalnya, informasi tentang hierarki warisan, tipe >, dan table splitting tidak ada di skema database. Karena itu, konstruksi-konstruksi ini tidak akan pernah di-scaffold.
  • Selain itu, sejumlah jenis kolom mungkin tidak didukung oleh penyedia EF Core. Kolom ini tidak disertakan dalam model.
  • Anda dapat menentukan token konkurensi dalam model EF Core untuk mencegah dua pengguna memperbarui entitas yang sama secara bersamaan. Beberapa database memiliki jenis khusus untuk mewakili jenis kolom ini (misalnya, rowversion di SQL Server) dalam hal ini kita dapat merekayasa balik informasi ini; namun, token konkurensi lainnya tidak akan di-scaffold.