Bagikan melalui


Bagian 4, Razor Halaman dengan EF Core migrasi di ASP.NET Core

Oleh Tom Dykstra, Jon P Smith, dan Rick Anderson

Aplikasi web Contoso University menunjukkan cara membuat Razor aplikasi web Pages menggunakan EF Core dan Visual Studio. Untuk informasi tentang seri tutorial, lihat tutorial pertama.

Jika Anda mengalami masalah yang tidak dapat Anda selesaikan, unduh aplikasi yang telah selesai dan bandingkan kode tersebut dengan apa yang Anda buat dengan mengikuti tutorial.

Tutorial ini memperkenalkan EF Core fitur migrasi untuk mengelola perubahan model data.

Saat aplikasi baru dikembangkan, model data sering berubah. Setiap kali model berubah, model tidak sinkron dengan database. Seri tutorial ini dimulai dengan mengonfigurasi Kerangka Kerja Entitas untuk membuat database jika tidak ada. Setiap kali model data berubah, database perlu dihilangkan. Lain kali aplikasi berjalan, panggilan untuk EnsureCreated membuat ulang database agar sesuai dengan model data baru. Kelas DbInitializer kemudian berjalan untuk menyemai database baru.

Pendekatan ini untuk menjaga DB tetap sinkron dengan model data berfungsi dengan baik sampai aplikasi perlu disebarkan ke produksi. Saat aplikasi berjalan dalam produksi, biasanya menyimpan data yang perlu dipertahankan. Aplikasi tidak dapat dimulai dengan uji DB setiap kali perubahan dilakukan (seperti menambahkan kolom baru). Fitur EF Core Migrasi memecahkan masalah ini dengan mengaktifkan EF Core pembaruan skema DB alih-alih membuat database baru.

Daripada menghilangkan dan membuat ulang database saat model data berubah, migrasi memperbarui skema dan menyimpan data yang ada.

Catatan

Batasan SQLite

Tutorial ini menggunakan fitur migrasi Entity Framework Core jika memungkinkan. Migrasi memperbarui skema database agar sesuai dengan perubahan dalam model data. Namun, migrasi hanya melakukan jenis perubahan yang didukung mesin database, dan kemampuan perubahan skema SQLite terbatas. Misalnya, menambahkan kolom didukung, tetapi menghapus kolom tidak didukung. Jika migrasi dibuat untuk menghapus kolom, ef migrations add perintah berhasil tetapi ef database update perintah gagal.

Solusi untuk batasan SQLite adalah menulis kode migrasi secara manual untuk melakukan pembangunan ulang tabel saat sesuatu dalam tabel berubah. Kode masuk dalam Up metode dan Down untuk migrasi dan melibatkan:

  • Membuat tabel baru.
  • Menyalin data dari tabel lama ke tabel baru.
  • Menjatuhkan tabel lama.
  • Mengganti nama tabel baru.

Menulis kode khusus database dari jenis ini berada di luar lingkup tutorial ini. Sebaliknya, tutorial ini menghilangkan dan membuat ulang database setiap kali upaya untuk menerapkan migrasi akan gagal. Untuk informasi selengkapnya, lihat sumber daya berikut:

Menghapus database

Gunakan SQL Server Object Explorer (SSOX) untuk menghapus database, atau jalankan perintah berikut di Konsol Manajer Paket (PMC):

Drop-Database

Membuat migrasi awal

Jalankan perintah berikut di PMC:

Add-Migration InitialCreate
Update-Database

Hapus EnsureCreated

Seri tutorial ini dimulai dengan menggunakan EnsureCreated. EnsureCreated tidak membuat tabel riwayat migrasi sehingga tidak dapat digunakan dengan migrasi. Ini dirancang untuk pengujian atau pembuatan prototipe cepat di mana database dihilangkan dan sering dibuat ulang.

Dari titik ini ke depan, tutorial akan menggunakan migrasi.

Di Program.cs, hapus baris berikut:

context.Database.EnsureCreated();

Jalankan aplikasi dan verifikasi bahwa database disemai.

Metode Naik dan Turun

Perintah EF Coremigrations add yang dihasilkan kode untuk membuat database. Kode migrasi ini ada dalam Migrations\<timestamp>_InitialCreate.cs file. Metode UpInitialCreate kelas membuat tabel database yang sesuai dengan kumpulan entitas model data. Metode Down menghapusnya, seperti yang ditunjukkan dalam contoh berikut:

using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;

namespace ContosoUniversity.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Course",
                columns: table => new
                {
                    CourseID = table.Column<int>(nullable: false),
                    Title = table.Column<string>(nullable: true),
                    Credits = table.Column<int>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Course", x => x.CourseID);
                });

            migrationBuilder.CreateTable(
                name: "Student",
                columns: table => new
                {
                    ID = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                    LastName = table.Column<string>(nullable: true),
                    FirstMidName = table.Column<string>(nullable: true),
                    EnrollmentDate = table.Column<DateTime>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Student", x => x.ID);
                });

            migrationBuilder.CreateTable(
                name: "Enrollment",
                columns: table => new
                {
                    EnrollmentID = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                    CourseID = table.Column<int>(nullable: false),
                    StudentID = table.Column<int>(nullable: false),
                    Grade = table.Column<int>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Enrollment", x => x.EnrollmentID);
                    table.ForeignKey(
                        name: "FK_Enrollment_Course_CourseID",
                        column: x => x.CourseID,
                        principalTable: "Course",
                        principalColumn: "CourseID",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_Enrollment_Student_StudentID",
                        column: x => x.StudentID,
                        principalTable: "Student",
                        principalColumn: "ID",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateIndex(
                name: "IX_Enrollment_CourseID",
                table: "Enrollment",
                column: "CourseID");

            migrationBuilder.CreateIndex(
                name: "IX_Enrollment_StudentID",
                table: "Enrollment",
                column: "StudentID");
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Enrollment");

            migrationBuilder.DropTable(
                name: "Course");

            migrationBuilder.DropTable(
                name: "Student");
        }
    }
}

Kode sebelumnya adalah untuk migrasi awal. Kode:

  • Dihasilkan oleh migrations add InitialCreate perintah .
  • Dijalankan oleh database update perintah .
  • Membuat database untuk model data yang ditentukan oleh kelas konteks database.

Parameter nama migrasi (InitialCreate dalam contoh) digunakan untuk nama file. Nama migrasi dapat berupa nama file yang valid. Yang terbaik adalah memilih kata atau frasa yang meringkas apa yang sedang dilakukan dalam migrasi. Misalnya, migrasi yang menambahkan tabel departemen mungkin disebut "AddDepartmentTable."

Tabel riwayat migrasi

  • Gunakan alat SSOX atau SQLite untuk memeriksa database.
  • Perhatikan penambahan __EFMigrationsHistory tabel. Tabel __EFMigrationsHistory melacak migrasi mana yang telah diterapkan ke database.
  • Lihat data dalam __EFMigrationsHistory tabel. Ini menunjukkan satu baris untuk migrasi pertama.

Rekam jepret model data

Migrasi membuat rekam jepret model data saat ini di Migrations/SchoolContextModelSnapshot.cs. Saat menambahkan migrasi ditambahkan, EF menentukan apa yang berubah dengan membandingkan model data saat ini dengan file rekam jepret.

Karena file rekam jepret melacak status model data, migrasi tidak dapat dihapus dengan menghapus <timestamp>_<migrationname>.cs file. Untuk mencadangkan migrasi terbaru, gunakan migrations remove perintah . migrations remove menghapus migrasi dan memastikan rekam jepret diatur ulang dengan benar. Untuk informasi selengkapnya, lihat penghapusan migrasi dotnet ef.

Lihat Mereset semua migrasi untuk menghapus semua migrasi.

Menerapkan migrasi dalam produksi

Sebaiknya aplikasi produksi tidak memanggil Database.Migrate saat pengaktifan aplikasi. Migrate tidak boleh dipanggil dari aplikasi yang disebarkan ke farm server. Jika aplikasi diskalakan ke beberapa instans server, sulit untuk memastikan pembaruan skema database tidak terjadi dari beberapa server atau konflik dengan akses baca/tulis.

Migrasi database harus dilakukan sebagai bagian dari penyebaran, dan dengan cara yang terkontrol. Pendekatan migrasi database produksi meliputi:

  • Menggunakan migrasi untuk membuat skrip SQL dan menggunakan skrip SQL dalam penyebaran.
  • Berjalan dotnet ef database update dari lingkungan terkontrol.

Pemecahan Masalah

Jika aplikasi menggunakan SQL Server LocalDB dan menampilkan pengecualian berikut:

SqlException: Cannot open database "ContosoUniversity" requested by the login.
The login failed.
Login failed for user 'user name'.

Solusinya mungkin dijalankan dotnet ef database update pada prompt perintah.

Sumber Daya Tambahan:

Langkah berikutnya

Tutorial berikutnya membangun model data, menambahkan properti entitas dan entitas baru.

Dalam tutorial ini, EF Core fitur migrasi untuk mengelola perubahan model data digunakan.

Jika Anda mengalami masalah yang tidak dapat Anda selesaikan, unduh aplikasi yang telah selesai.

Saat aplikasi baru dikembangkan, model data sering berubah. Setiap kali model berubah, model tidak sinkron dengan database. Tutorial ini dimulai dengan mengonfigurasi Kerangka Kerja Entitas untuk membuat database jika tidak ada. Setiap kali model data berubah:

  • DB dihilangkan.
  • EF membuat yang baru yang cocok dengan model.
  • Aplikasi ini menyemai DB dengan data pengujian.

Pendekatan ini untuk menjaga DB tetap sinkron dengan model data berfungsi dengan baik sampai aplikasi perlu disebarkan ke produksi. Saat aplikasi berjalan dalam produksi, biasanya menyimpan data yang perlu dipertahankan. Aplikasi tidak dapat dimulai dengan uji DB setiap kali perubahan dilakukan (seperti menambahkan kolom baru). Fitur EF Core Migrasi memecahkan masalah ini dengan mengaktifkan EF Core pembaruan skema DB alih-alih membuat DB baru.

Daripada menghilangkan dan membuat ulang DB saat model data berubah, migrasi memperbarui skema dan menyimpan data yang ada.

Menghapus database

Gunakan SQL Server Object Explorer (SSOX) atau database drop perintah :

Di Package Manager Console (PMC), jalankan perintah berikut:

Drop-Database

Jalankan Get-Help about_EntityFrameworkCore dari PMC untuk mendapatkan informasi bantuan.

Membuat migrasi awal dan memperbarui DB

Bangun proyek dan buat migrasi pertama.

Add-Migration InitialCreate
Update-Database

Memeriksa metode Naik dan Turun

Perintah EF Coremigrations add yang dihasilkan kode untuk membuat database. Kode migrasi ini ada dalam Migrations\<timestamp>_InitialCreate.cs file. Metode UpInitialCreate kelas membuat tabel database yang sesuai dengan kumpulan entitas model data. Metode Down menghapusnya, seperti yang ditunjukkan dalam contoh berikut:

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Course",
            columns: table => new
            {
                CourseID = table.Column<int>(nullable: false),
                Title = table.Column<string>(nullable: true),
                Credits = table.Column<int>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Course", x => x.CourseID);
            });

        migrationBuilder.CreateTable(
    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Enrollment");

        migrationBuilder.DropTable(
            name: "Course");

        migrationBuilder.DropTable(
            name: "Student");
    }
}

Migrasi memanggil Up metode untuk menerapkan perubahan model data untuk migrasi. Saat perintah dimasukkan untuk mengembalikan pembaruan, migrasi memanggil Down metode .

Kode sebelumnya adalah untuk migrasi awal. Kode itu dibuat ketika migrations add InitialCreate perintah dijalankan. Parameter nama migrasi ("InitialCreate" dalam contoh) digunakan untuk nama file. Nama migrasi dapat berupa nama file yang valid. Yang terbaik adalah memilih kata atau frasa yang meringkas apa yang sedang dilakukan dalam migrasi. Misalnya, migrasi yang menambahkan tabel departemen mungkin disebut "AddDepartmentTable."

Jika migrasi awal dibuat dan DB ada:

  • Kode pembuatan DB dihasilkan.
  • Kode pembuatan DB tidak perlu dijalankan karena DB sudah cocok dengan model data. Jika kode pembuatan DB dijalankan, kode tersebut tidak membuat perubahan apa pun karena DB sudah cocok dengan model data.

Saat aplikasi disebarkan ke lingkungan baru, kode pembuatan DB harus dijalankan untuk membuat DB.

Sebelumnya DB dihilangkan dan tidak ada, sehingga migrasi membuat DB baru.

Rekam jepret model data

Migrasi membuat rekam jepret skema database saat ini di Migrations/SchoolContextModelSnapshot.cs. Saat Anda menambahkan migrasi, EF menentukan apa yang berubah dengan membandingkan model data dengan file rekam jepret.

Untuk menghapus migrasi, gunakan perintah berikut:

Hapus-Migrasi

Perintah hapus migrasi menghapus migrasi dan memastikan rekam jepret diatur ulang dengan benar.

Menghapus EnsureCreated dan menguji aplikasi

Untuk pengembangan awal, EnsureCreated digunakan. Dalam tutorial ini, migrasi digunakan. EnsureCreated memiliki batasan berikut:

  • Melewati migrasi dan membuat DB dan skema.
  • Tidak membuat tabel migrasi.
  • Tidak dapat digunakan dengan migrasi.
  • Dirancang untuk pengujian atau pembuatan prototipe cepat di mana DB sering dihilangkan dan dibuat ulang.

Hapus EnsureCreated:

context.Database.EnsureCreated();

Jalankan aplikasi dan verifikasi bahwa DB adalah seeded.

Memeriksa database

Gunakan SQL Server Object Explorer untuk memeriksa DB. Perhatikan penambahan __EFMigrationsHistory tabel. Tabel __EFMigrationsHistory melacak migrasi mana yang telah diterapkan ke DB. Menampilkan data dalam __EFMigrationsHistory tabel, data memperlihatkan satu baris untuk migrasi pertama. Log terakhir dalam contoh output CLI sebelumnya menunjukkan pernyataan INSERT yang membuat baris ini.

Jalankan aplikasi dan verifikasi bahwa semuanya berfungsi.

Menerapkan migrasi dalam produksi

Sebaiknya aplikasi produksi tidak boleh memanggil Database.Migrate saat pengaktifan aplikasi. Migrate tidak boleh dipanggil dari aplikasi di farm server. Misalnya, jika aplikasi telah disebarkan cloud dengan peluasan skala (beberapa instans aplikasi sedang berjalan).

Migrasi database harus dilakukan sebagai bagian dari penyebaran, dan dengan cara yang terkontrol. Pendekatan migrasi database produksi meliputi:

  • Menggunakan migrasi untuk membuat skrip SQL dan menggunakan skrip SQL dalam penyebaran.
  • Berjalan dotnet ef database update dari lingkungan terkontrol.

EF Core__MigrationsHistory menggunakan tabel untuk melihat apakah ada migrasi yang perlu dijalankan. Jika DB sudah diperbarui, tidak ada migrasi yang dijalankan.

Pemecahan Masalah

Unduh aplikasi yang telah selesai.

Aplikasi ini menghasilkan pengecualian berikut:

SqlException: Cannot open database "ContosoUniversity" requested by the login.
The login failed.
Login failed for user 'user name'.

Solusi: Jalankan dotnet ef database update

Sumber Daya Tambahan: