Bagikan melalui


Kode Migrasi Pertama

Migrasi Code First adalah cara yang direkomendasikan untuk mengembangkan skema database aplikasi jika Anda menggunakan alur kerja Code First. Migrasi menyediakan sekumpulan alat yang memungkinkan:

  1. Membuat database awal yang berfungsi dengan model EF Anda
  2. Menghasilkan migrasi untuk melacak perubahan yang Anda buat pada model EF Anda
  3. Selalu memperbarui database Anda dengan perubahan tersebut

Panduan berikut akan memberikan gambaran umum Migrasi Code First dalam Entity Framework. Anda dapat menyelesaikan seluruh panduan atau melompat ke topik yang Anda minati. Topik yang dibahas:

Membangun Model Awal & Database

Sebelum mulai menggunakan migrasi, kita memerlukan proyek dan model Code First untuk dikerjakan. Untuk panduan ini kita akan menggunakan model Blog dan Posting kanonis.

  • Buat aplikasi MigrationsDemo Console baru
  • Menambahkan versi terbaru paket EntityFramework NuGet ke proyek
    • Alat –> Manajer Paket Pustaka –> Konsol Manajer Paket
    • Jalankan perintah Install-Package EntityFramework
  • Tambahkan file Model.cs dengan kode yang ditunjukkan di bawah ini. Kode ini menentukan satu kelas Blog yang membentuk model domain kami dan kelas BlogContext yang merupakan konteks Code First EF kami
    using System.Data.Entity;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Data.Entity.Infrastructure;

    namespace MigrationsDemo
    {
        public class BlogContext : DbContext
        {
            public DbSet<Blog> Blogs { get; set; }
        }

        public class Blog
        {
            public int BlogId { get; set; }
            public string Name { get; set; }
        }
    }
  • Sekarang setelah kita memiliki model, saatnya untuk menggunakannya untuk melakukan akses data. Perbarui file Program.cs dengan kode yang ditunjukkan di bawah ini.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace MigrationsDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (var db = new BlogContext())
                {
                    db.Blogs.Add(new Blog { Name = "Another Blog " });
                    db.SaveChanges();

                    foreach (var blog in db.Blogs)
                    {
                        Console.WriteLine(blog.Name);
                    }
                }

                Console.WriteLine("Press any key to exit...");
                Console.ReadKey();
            }
        }
    }
  • Jalankan aplikasi Anda dan Anda akan melihat bahwa database MigrationsCodeDemo.BlogContext dibuat untuk Anda.

    Database LocalDB

Mengaktifkan Migrasi

Saatnya untuk membuat beberapa perubahan lagi pada model kami.

  • Mari kita perkenalkan properti Url ke kelas Blog.
    public string Url { get; set; }

Jika Anda menjalankan aplikasi lagi, Anda akan mendapatkan InvalidOperationException yang menyatakan Model yang mendukung konteks 'BlogContext' telah berubah sejak database dibuat. Pertimbangkan untuk menggunakan Migrasi Code First untuk memperbarui database (http://go.microsoft.com/fwlink/?LinkId=238269).

Seperti yang disarankan pengecualian, saatnya untuk mulai menggunakan Migrasi Code First. Langkah pertama adalah mengaktifkan migrasi untuk konteks kami.

  • Jalankan perintah Enable-Migrations di Konsol Manajer Paket

    Perintah ini telah menambahkan folder Migrasi ke proyek kami. Folder baru ini berisi dua file:

  • Kelas Konfigurasi. Kelas ini memungkinkan Anda mengonfigurasi perilaku Migrasi untuk konteks Anda. Untuk panduan ini, kami hanya akan menggunakan konfigurasi default. Karena hanya ada satu konteks Code First dalam proyek Anda, Enable-Migrations telah secara otomatis mengisi jenis konteks yang diterapkan konfigurasi ini.

  • Migrasi InitialCreate. Migrasi ini dibuat karena kami sudah memiliki Code First yang membuat database untuk kami, sebelum kami mengaktifkan migrasi. Kode dalam migrasi yang dirancah ini mewakili objek yang telah dibuat dalam database. Dalam kasus kami, tabel Blog dengan kolom BlogId dan Nama. Nama file menyertakan tanda waktu untuk membantu pemesanan. Jika database belum dibuat, migrasi InitialCreate ini tidak akan ditambahkan ke proyek. Sebaliknya, pertama kali kami memanggil Add-Migration, kode untuk membuat tabel ini akan dirancah ke migrasi baru.

Beberapa Model Menargetkan Database yang Sama

Saat menggunakan versi sebelum EF6, hanya satu model Code First yang dapat digunakan untuk menghasilkan/mengelola skema database. Ini adalah hasil dari satu tabel __MigrationsHistory per database tanpa cara untuk mengidentifikasi entri mana yang termasuk dalam model mana.

Dimulai dengan EF6, kelas Konfigurasi menyertakan properti ContextKey. Ini bertindak sebagai pengidentifikasi unik untuk setiap model Code First. Kolom terkait dalam tabel __MigrationsHistory memungkinkan entri dari beberapa model untuk berbagi tabel. Secara default, properti ini diatur ke nama konteks Anda yang sepenuhnya memenuhi syarat.

Menghasilkan & Menjalankan Migrasi

Migrasi Code First memiliki dua perintah utama yang akan Anda kenal.

  • Add-Migration akan membuat perancah migrasi berikutnya berdasarkan perubahan yang telah Anda buat pada model Anda sejak migrasi terakhir dibuat
  • Update-Database akan menerapkan migrasi yang tertunda ke database

Kita perlu membuat perancah migrasi untuk mengurus properti Url baru yang telah kita tambahkan. Perintah Add-Migration memungkinkan kita untuk memberi nama migrasi ini, mari kita panggil AddBlogUrl kita.

  • Jalankan perintah Add-Migration AddBlogUrl di Konsol Manajer Paket
  • Di folder Migrasi, kami sekarang memiliki migrasi AddBlogUrl baru. Nama file migrasi telah diperbaiki sebelumnya dengan tanda waktu untuk membantu pemesanan
    namespace MigrationsDemo.Migrations
    {
        using System;
        using System.Data.Entity.Migrations;

        public partial class AddBlogUrl : DbMigration
        {
            public override void Up()
            {
                AddColumn("dbo.Blogs", "Url", c => c.String());
            }

            public override void Down()
            {
                DropColumn("dbo.Blogs", "Url");
            }
        }
    }

Kita sekarang dapat mengedit atau menambahkan ke migrasi ini tetapi semuanya terlihat cukup baik. Mari kita gunakan Update-Database untuk menerapkan migrasi ini ke database.

  • Jalankan perintah Update-Database di Konsol Manajer Paket
  • Migrasi Code First akan membandingkan migrasi di folder Migrasi kami dengan yang telah diterapkan ke database. Migrasi ini akan melihat bahwa migrasi AddBlogUrl perlu diterapkan, dan menjalankannya.

Database MigrationsDemo.BlogContext sekarang diperbarui untuk menyertakan kolom Url dalam tabel Blog.

Menyesuaikan Migrasi

Sejauh ini kami telah menghasilkan dan menjalankan migrasi tanpa membuat perubahan apa pun. Sekarang mari lihat proses mengedit kode yang dihasilkan secara default.

  • Saatnya untuk membuat beberapa perubahan lagi pada model kita, mari tambahkan properti Peringkat baru ke kelas Blog
    public int Rating { get; set; }
  • Mari tambahkan juga kelas Posting baru
    public class Post
    {
        public int PostId { get; set; }
        [MaxLength(200)]
        public string Title { get; set; }
        public string Content { get; set; }

        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }
  • Kami juga akan menambahkan koleksi Posting ke kelas Blog untuk membentuk akhir hubungan lainnya antara Blog dan Posting
    public virtual List<Post> Posts { get; set; }

Kita akan menggunakan perintah Add-Migration untuk membiarkan Migrasi Code First merancang tebakan terbaiknya pada migrasi untuk kita. Kita akan menyebut migrasi ini AddPostClass.

  • Jalankan perintah Add-Migration AddPostClass di Konsol Manajer Paket.

Migrasi Code First melakukan pekerjaan yang cukup baik dalam perancah perubahan ini, tetapi ada beberapa hal yang mungkin ingin kita ubah:

  1. Pertama, mari kita tambahkan indeks unik ke kolom Posts.Title (Menambahkan di baris 22 & 29 dalam kode di bawah).
  2. Kami juga menambahkan kolom Blogs.Rating yang tidak dapat diubah ke null. Jika ada data yang ada dalam tabel, data akan mendapat default CLR dari jenis data untuk kolom baru (Peringkat adalah bilangan bulat, sehingga akan menjadi 0). Tetapi kita ingin menentukan nilai default 3 sehingga baris yang ada dalam tabel Blog akan dimulai dengan peringkat yang layak. (Anda dapat melihat nilai default yang ditentukan pada baris 24 dari kode di bawah)
    namespace MigrationsDemo.Migrations
    {
        using System;
        using System.Data.Entity.Migrations;

        public partial class AddPostClass : DbMigration
        {
            public override void Up()
            {
                CreateTable(
                    "dbo.Posts",
                    c => new
                        {
                            PostId = c.Int(nullable: false, identity: true),
                            Title = c.String(maxLength: 200),
                            Content = c.String(),
                            BlogId = c.Int(nullable: false),
                        })
                    .PrimaryKey(t => t.PostId)
                    .ForeignKey("dbo.Blogs", t => t.BlogId, cascadeDelete: true)
                    .Index(t => t.BlogId)
                    .Index(p => p.Title, unique: true);

                AddColumn("dbo.Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3));
            }

            public override void Down()
            {
                DropIndex("dbo.Posts", new[] { "Title" });
                DropIndex("dbo.Posts", new[] { "BlogId" });
                DropForeignKey("dbo.Posts", "BlogId", "dbo.Blogs");
                DropColumn("dbo.Blogs", "Rating");
                DropTable("dbo.Posts");
            }
        }
    }

Migrasi yang telah diedit siap digunakan, jadi mari gunakan Update-Database untuk memperbarui database. Kali ini mari tentukan bendera –Verbose sehingga Anda dapat melihat SQL yang dijalankan Migrasi Code First.

  • Jalankan perintah Update-Database –Verbose di Konsol Manajer Paket.

Gerakan Data/SQL Kustom

Sejauh ini kita telah melihat operasi migrasi yang tidak mengubah atau memindahkan data apa pun, sekarang mari kita lihat sesuatu yang perlu memindahkan beberapa data. Belum ada dukungan asli untuk gerakan data, tetapi kami dapat menjalankan beberapa perintah SQL arbitrer kapan saja dalam skrip kami.

  • Mari kita tambahkan properti Post.Abstract ke model kita. Nantinya, kita akan mengisi Abstrak terlebih dahulu untuk postingan yang ada menggunakan beberapa teks dari awal kolom Konten.
    public string Abstract { get; set; }

Kita akan menggunakan perintah Add-Migration untuk membiarkan Migrasi Code First merancang tebakan terbaiknya pada migrasi untuk kita.

  • Jalankan perintah Add-Migration AddPostAbstract di Konsol Manajer Paket.
  • Migrasi yang dihasilkan menangani perubahan skema tetapi kami juga ingin mengisi kolom Abstrak terlebih dahulu menggunakan 100 karakter konten pertama untuk setiap postingan. Kita dapat melakukan ini dengan turun ke SQL dan menjalankan pernyataan UPDATE setelah kolom ditambahkan. (Menambahkan baris 12 dalam kode di bawah)
    namespace MigrationsDemo.Migrations
    {
        using System;
        using System.Data.Entity.Migrations;

        public partial class AddPostAbstract : DbMigration
        {
            public override void Up()
            {
                AddColumn("dbo.Posts", "Abstract", c => c.String());

                Sql("UPDATE dbo.Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");
            }

            public override void Down()
            {
                DropColumn("dbo.Posts", "Abstract");
            }
        }
    }

Migrasi yang diedit terlihat bagus, jadi mari gunakan Update-Database untuk memperbarui database. Kita akan menentukan bendera –Verbose sehingga kita dapat melihat SQL dijalankan terhadap database.

  • Jalankan perintah Update-Database –Verbose di Konsol Manajer Paket.

Bermigrasi ke Versi Tertentu (Termasuk Penurunan Tingkat)

Sejauh ini kami selalu meningkatkan ke migrasi terbaru, tetapi mungkin ada kalanya Anda ingin meningkatkan/menurunkan tingkat ke migrasi tertentu.

Katakanlah kita ingin memigrasikan database kita ke statusnya setelah menjalankan migrasi AddBlogUrl kita. Kita dapat menggunakan sakelar –TargetMigration untuk menurunkan tingkat ke migrasi ini.

  • Jalankan perintah Update-Database –TargetMigration: AddBlogUrl di Konsol Manajer Paket.

Perintah ini akan menjalankan skrip Tidak Berfungsi untuk migrasi AddBlogAbstract dan AddPostClass kita.

Jika Anda ingin kembali ke database kosong maka Anda bisa menggunakan perintah Update-Database –TargetMigration: $InitialDatabase.

Mendapatkan Skrip SQL

Jika pengembang lain menginginkan perubahan ini pada komputer mereka, mereka dapat menyinkronkan begitu kami memeriksa perubahan kami ke kontrol sumber. Setelah mereka memiliki migrasi baru, mereka hanya dapat menjalankan perintah Update-Database agar perubahan diterapkan secara lokal. Namun jika kita ingin mendorong perubahan ini ke server pengujian, dan akhirnya produksi, kita mungkin menginginkan skrip SQL yang dapat kita serahkan ke DBA kita.

  • Jalankan perintah Update-Database tetapi kali ini tentukan bendera –Script sehingga perubahan ditulis ke skrip daripada diterapkan. Kami juga akan menentukan migrasi sumber dan target untuk membuat skrip. Kita ingin skrip beralih dari database kosong ($InitialDatabase) ke versi terbaru (migrasi AddPostAbstract). Jika Anda tidak menentukan migrasi target, Migrasi akan menggunakan migrasi terbaru sebagai target. Jika Anda tidak menentukan migrasi sumber, Migrasi akan menggunakan status database saat ini.
  • Jalankan perintah Update-Database -Script -SourceMigration: $InitialDatabase -TargetMigration: AddPostAbstract di Konsol Manajer Paket

Migrasi Code First akan menjalankan alur migrasi tetapi alih-alih benar-benar menerapkan perubahan, migrasi akan menuliskannya ke file .sql untuk Anda. Setelah skrip dibuat, skrip dibuka untuk Anda di Visual Studio, siap untuk Anda lihat atau simpan.

Menghasilkan Skrip Idempotensi

Dimulai dengan EF6, jika Anda menentukan –SourceMigration $InitialDatabase, skrip yang dihasilkan akan menjadi 'idempotensi'. Skrip idempotensi dapat meningkatkan database saat ini di versi apa pun ke versi terbaru (atau versi yang ditentukan jika Anda menggunakan –TargetMigration). Skrip yang dihasilkan menyertakan logika untuk memeriksa tabel __MigrationsHistory dan hanya menerapkan perubahan yang belum pernah diterapkan sebelumnya.

Meningkatkan Secara Otomatis pada Startup Aplikasi (Penginisialisasi MigrateDatabaseToLatestVersion)

Jika Anda menyebarkan aplikasi, Anda mungkin ingin secara otomatis meningkatkan database (dengan menerapkan migrasi yang tertunda) saat aplikasi diluncurkan. Anda dapat melakukannya dengan mendaftarkan penginisialisasi database MigrateDatabaseToLatestVersion. Penginisialisasi database hanya berisi beberapa logika yang digunakan untuk memastikan database diatur dengan benar. Logika ini dijalankan saat pertama kali konteks digunakan dalam proses aplikasi (AppDomain).

Kita dapat memperbarui file Program.cs, seperti yang ditunjukkan di bawah ini, untuk mengatur penginisialisasi MigrateDatabaseToLatestVersion untuk BlogContext sebelum kita menggunakan konteks (Baris 14). Perhatikan bahwa Anda juga perlu menambahkan pernyataan using untuk namespace System.Data.Entity (Baris 5).

Ketika kita membuat instans penginisialisasi ini, kita perlu menentukan jenis konteks (BlogContext) dan konfigurasi migrasi (Konfigurasi) - konfigurasi migrasi adalah kelas yang ditambahkan ke folder Migrasi ketika kita mengaktifkan Migrasi.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Data.Entity;
    using MigrationsDemo.Migrations;

    namespace MigrationsDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                Database.SetInitializer(new MigrateDatabaseToLatestVersion<BlogContext, Configuration>());

                using (var db = new BlogContext())
                {
                    db.Blogs.Add(new Blog { Name = "Another Blog " });
                    db.SaveChanges();

                    foreach (var blog in db.Blogs)
                    {
                        Console.WriteLine(blog.Name);
                    }
                }

                Console.WriteLine("Press any key to exit...");
                Console.ReadKey();
            }
        }
    }

Sekarang, setiap kali aplikasi kami berjalan, aplikasi pertama-tama akan memeriksa apakah database yang ditargetkannya sudah diperbarui, dan menerapkan migrasi yang tertunda jika tidak.