Migration trong Entity Framework: Khởi tạo và cập nhật CSDL

    0

    Migration là một cơ chế đặc biệt trong Entity Framework cho phép cập nhật CSDL theo cấu trúc model class mà không làm mất dữ liệu hiện có. Đây cũng là cơ chế phải sử dụng đến nếu trong CSDL thử nghiệm của bạn có nhiều dữ liệu và bạn không muốn hủy bỏ CSDL cũ để làm lại cái mới.

    Bài học này sẽ hướng dẫn các bạn các phương pháp sử dụng Migration trong project cùng với Entity Framework code-first. Cụ thể, bạn sẽ học cả hai cách thực hiện migration, bao gồm migration tự động (automated migration) và migration sử dụng code (code-based migration). Bạn cũng sẽ học cách hủy cập nhật CSDL (rollback migration) để quay lại phiên bản cũ của CSDL.

    Quay trở lại Tự học lập trình ADO.NET và Entity Framework

    Migration trong Entity Framework

    Trong bài học trước bạn đã biết Entity Framework code-first có khả năng tự động khởi tạo CSDL với các bộ khởi tạo như CreateDatabaseIfNotExists, DropCreateDatabaseIfModelChanges hay DropCreateDatabaseAlways. Các bộ khởi tạo này giúp bạn đối phó với tình huống thay đổi cấu trúc model class.

    Tuy nhiên các bộ khởi tạo này có một vấn đề nghiêm trọng: chúng xóa bỏ CSDL cũ và tạo CSDL theo cấu trúc class mới.

    Nếu CSDL của bạn có dữ liệu, hàm lưu (stored procedure), hay trigger, những thứ này cũng sẽ mất đi khi CSDL bị xóa. Do đó, các chiến lược khởi tạo trên không phù hợp trong tình huống này.

    Entity Framework giới thiệu một công cụ mới giúp tự động cập nhật cấu trúc cơ sở dữ liệu nếu lớp model thay đổi mà không làm mất dữ liệu và các object trong đó. Công cụ này được gọi là migration.

    Migration sử dụng một bộ khởi tạo mới có tên là MigrateDatabaseToLatestVersion.

    Khi sử dụng migration bạn có thể lựa chọn một trong các phương án:

    • Migration tự động (automatic migration)
    • Migration thủ công dùng lệnh (code-based migration).

    Do migration không được tự động sử dụng nên dù theo phương án nào bạn cũng phải tự mình kích hoạt công cụ migration cho project.

    Để kích hoạt migration cho một project, bạn mở cửa sổ Package Manager Console sau đó chọn Default project là project chứa lớp context. Package Manager Console mở từ Tools Library Package ManagerPackage Manager Console.

    Từ dấu nhắc lệnh PM> gõ lệnh enable-migrations cùng với tham số phù hợp. Tùy thuộc vào giá trị tham số, Entity Framework sẽ xác định được bạn đang muốn dùng phương án nào.

    Tạm thời bạn đừng chạy lệnh enable-migrations. Bạn sẽ thực hiện lệnh phù hợp khi học về từng loại migration trong các phần tiếp theo. Hãy lưu ý rằng, bạn chỉ có thể kích hoạt migration trong project có khai báo lớp context. Nếu bạn lựa chọn project khác, lệnh kích hoạt migration sẽ báo lỗi.

    Tiếp theo đây bạn sẽ học cách làm việc với cả hai loại migration này.

    Automatic Migration trong Entity Framework

    Khi sử dụng migration tự động bạn không phải tự mình chạy công cụ migration mỗi lần thay đổi domain class.

    Hãy cùng thực hiện một ví dụ.

    Chuẩn bị project thử nghiệm

    Tạo blank solution S07_Migration. Trong đó tạo project mới P01_AutomatedMigration và cài đặt Entity Framework cho project này (sử dụng lệnh install-package entityframework trong Package Manager Console). Bạn không cần làm gì khác.

    Viết code cho Program.cs như sau:

    using System;
    using System.Data.Entity;
    using System.Linq;
    
    namespace P01_AutomatedMigration
    {
        public class Person
        {
            public int Id { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
            
        }
        public class Context : DbContext
        {
            public Context() : base("AutomatedMigration")
            {                    
            }
    
            public DbSet<Person> People { get; set; }
        }
        class Program
        {
            static void Main(string[] args)
            {
                using (var context = new Context())
                {
                    context.People.AddRange(new[]
                    {
                        new Person{FirstName = "Donald", LastName = "Trump"},
                        new Person{FirstName = "Barack", LastName = "Obama"}
                    });
                    context.SaveChanges();
    
                    var people = context.People.ToArray();
                    foreach (var p in people)
                    {
                        Console.WriteLine($"{p.FirstName} {p.LastName}");
                    }
                }
    
                Console.ReadKey();
            }
        }
    }

    Code này giống hệt những gì bạn đã thực hiện ở bài trước.

    Giờ nếu bạn chạy chương trình, một CSDL mới với tên gọi AutomatedMigration sẽ được tạo ra với 2 bảng People và __MigrationHistory (trên LocalDb hoặc instance mặc định). Bảng People chỉ có 3 cột Id, FirstName và LastName.

    Kích hoạt automatic migration

    Để kích hoạt chế độ migration tự động, bạn sử dụng lệnh enable-migrations –EnableAutomaticMigration:$true trong Package Manager Console như sau:

    Lưu ý rằng, Default project phải là project chứa lớp context. Trong ví dụ này chúng ta chỉ có 1 project và lớp Context nằm ở đây.

    Nếu migration được kích hoạt thành công bạn sẽ thu được kết quả như sau:

    Thư mục mới Migrations chứa file code Configuration.cs sẽ được tạo ra trong project. File code này chứa sealed class Configuration kế thừa từ DbMigrationConfiguration. Constructor của class này chứa một lệnh duy nhất AutomaticMigrationsEnabled = true báo hiệu chế độ automatic migration đang được sử dụng.

    Sử dụng bộ khởi tạo cùng lớp Configuration

    Điều chỉnh constructor của lớp Context như sau:

    public Context() : base("AutomatedMigration")
    {
        var initializer = new MigrateDatabaseToLatestVersion<Context, Migrations.Configuration>();
        Database.SetInitializer(initializer);
    }

    Ở bước này bạn tạo ra một bộ khởi tạo từ lớp MigrateDatabaseToLatestVersion sử dụng lớp Configuration ở trên và truyền bộ khởi tạo này cho phương thức SetInitializer như đã biết trong bài trước.

    Cập nhật thêm property vào lớp Person

    Giờ bạn thay đổi lớp Person như sau:

    public class Person
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string MiddleName { get; set; }
        public DateTime DateOfBirth { get; set; }
    }

    và điều chỉnh lại phương thức Main một chút (bỏ phần code thêm dữ liệu):

    static void Main(string[] args)
    {
        using (var context = new Context())
        {
            //context.People.AddRange(new[]
            //{
            //    new Person{FirstName = "Donald", LastName = "Trump"},
            //    new Person{FirstName = "Barack", LastName = "Obama"}
            //});
            //context.SaveChanges();
    
            var people = context.People.ToArray();
            foreach (var p in people)
            {
                Console.WriteLine($"{p.FirstName} {p.LastName}");
            }
        }
    
        Console.ReadKey();
    }

    Chạy chương trình và kiểm tra kết quả

    Khi chạy lại chương trình, bạn sẽ thấy CSDL đã được tự động cập nhật với 2 trường mới nhưng dữ liệu cũ của bạn không mất đi.

    Nếu mở bảng __MigrationHistory bạn sẽ thấy nội dung đại khái như sau:

    Bảng này lưu lại lịch sử thực hiện migration (cập nhật CSDL) từ project của bạn.

    Như bạn đã thấy, toàn bộ quá trình cập nhật cấu trúc CSDL diễn ra hoàn toàn tự động. Bạn cũng không bị mất các dữ liệu đã có. Như vậy có thể thấy, cơ chế migration không xóa – tạo lại CSDL mà là cập nhật cấu trúc CSDL theo cấu trúc domain class.

    Xóa property từ lớp domain class

    Trong tình huống ở trên, bạn thêm property mới vào lớp Person. Migration dễ dàng cập nhật CSDL mà không làm mất dữ liệu cũ. Migration cũng hoạt động tương tự nếu bạn thêm domain class mới.

    Tuy nhiên, nếu bạn xóa bỏ một vài property, thay đổi tên property, hoặc xóa domain class rồi chạy lại chương trình, migration sẽ báo lỗi:

    Vấn đề nằm ở chỗ, khi xóa bỏ/thay đổi property hoặc xóa bỏ domain class sẽ dẫn đến mất dữ liệu. Migration mặc định sẽ cấm tất cả các hoạt động cập nhật nếu gây mất dữ liệu.

    Bạn có thể thay đổi cách làm mặc định của migration bằng cách điều chỉnh constructor của lớp Configuration như sau:

    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = true;
    }

    Ở đây bạn thêm lệnh điều chỉnh property AutomaticMigrationDataLossAllowed thành giá trị true. Thông tin này báo cho migration rằng bạn chấp nhận mất một phần dữ liệu khi thực hiện thay đổi CSDL. Bạn sẽ tự chịu trách nhiệm cho việc mất một phần dữ liệu liên quan.

    Code-Based Migration trong Entity Framework

    Sử dụng migration qua code một cách thủ công cho phép bạn kiểm soát quá trình cập nhập CSDL cũng như thực hiện một số cấu hình bổ sung (như giá trị mặc định của cột, cột tính giá trị tự động, v.v.).

    Tạo project thử nghiệm

    Hãy tạo project mới trong solution lấy tên là P02_ManualMigration để thử nghiệm. Viết code cho Program.cs như sau:

    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace P02_ManualMigration
    {
        public class Context : DbContext
        {
            public Context() : base("ManualMigration")
            {
               
            }        
        }
        class Program
        {
            static void Main(string[] args)
            {
                
            }
        }
    }

    Kích hoạt chế độ migration thủ công

    Tương tự như trên, để kích hoạt chế độ migration thủ công bạn chỉ cần chạy lệnh Enable-Migrations từ Package Manager Console. Lưu ý rằng, lệnh này không cần tham số.

    Nếu kích hoạt thành công, bạn thu được kết quả thu được giống như trường hợp trên, ngoại trừ AutomaticMigrationEnabled = false.

    Điều chỉnh constructor của Context tương tự như trong ví dụ trước:

    public Context() : base("ManualMigration")
    {
        var initializer = new MigrateDatabaseToLatestVersion<Context, Migrations.Configuration>();
        Database.SetInitializer(initializer);
    }

    Tuy nhiên, bạn cần tiếp tục sử dụng Package Manager Console để thực hiện các lệnh sau:

    Thực hiện lệnh Add-Migration

    Lệnh Add-Migration yêu cầu tạo ra một class mới để giúp thực hiện cập nhật CSDL. Bạn gọi lệnh này sau khi thực hiện các thay đổi trên các domain class (như thêm/sửa/xóa property và class). Đây là lệnh trung gian cần thực hiện trước khi yêu cầu cập nhật CSDL.

    Lệnh này sẽ tạo ra một class mới có tên do bạn cung cấp (tham số Name khi thực hiện lệnh). Trong ví dụ trên, lớp mới có tên là v1 và kế thừa từ lớp DbMigration.

    Class này được đặt trong file code có phần đầu là thời gian tạo file và phần sau là tên class.

    Lớp này ghi đè hai phương thức Up() và Down(). Phương thức Up() sẽ được gọi khi bạn chạy lệnh tiếp theo (Update-Database). Trong phương thức này sẽ chứa các lệnh thay đổi cấu trúc CSDL.

    Trong ví dụ trên phương thức Up() không có nội dung gì. Lý do là lớp Context thậm chí còn chưa có property nào. Bạn cũng chưa xây dựng model class nào.

    Thực hiện lệnh Update-Database

    Lệnh Update-Database yêu cầu thực thi cập nhật CSDL. Lệnh này chỉ được gọi sau khi thực hiện thành công Add-Migration.

    Trong ví dụ trên, nếu bạn gọi Update-Database sẽ thu được kết quả là CSDL sau:

    Trong tình huống này migration thủ công chỉ giúp bạn tạo ra CSDL vì bạn chưa từng tạo model class nào và cũng không có property tương ứng trong Context. Ít nhất bạn cũng thấy được rằng manual migration giúp bạn tạo CSDL theo yêu cầu thay vì phụ thuộc vào lúc chạy chương trình.

    Nhiều bạn thích cách tạo CSDL này hơn là phụ thuộc vào bộ khởi tạo hoặc dùng lệnh khởi tạo CreateIfNotExists. Bạn có thể chủ động quyết định khởi tạo hoặc cập nhật CSDL vào thời điểm mình cần.

    Cập nhật CSDL

    Bạn bổ sung thêm lớp Person và property People cho lớp Context như sau:

    using System;
    using System.Data.Entity;
    
    namespace P02_ManualMigration
    {
        public class Person
        {
            public int Id { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public string MiddleName { get; set; }
            public DateTime DateOfBirth { get; set; }
            public string Alias { get; set; }
        }
    
        public class Context : DbContext
        {
            public Context() : base("ManualMigration")
            {
                var initializer = new MigrateDatabaseToLatestVersion<Context, Migrations.Configuration>();
                Database.SetInitializer(initializer);
            }
            public DbSet<Person> People { get; set; }
        }
    
        internal class Program
        {
            private static void Main(string[] args)
            {
            }
        }
    }

    Bạn chạy lại hai lệnh add-migration:

    Hãy để ý tới thân của phương thức Up() và Down().

    Giờ gọi lệnh update-database:

    Bạn thu được CSDL cập nhật như sau:

    Hẳn bạn dễ dàng nhận quy trình: bạn thay đổi domain class => gọi lệnh add-migration => gọi lệnh update-database.

    Giờ đây tất cả quá trình tạo/cập nhật CSDL do bạn chủ động quyết định. Bạn cũng hạn chế tối đa mất dữ liệu. Đây có thể nói là cơ chế tạo và cập nhật CSDL tốt nhất và bạn nên sử dụng nó.

    Sử dụng chế độ “lai”:

    Thực ra bạn thậm chí còn có thể sử dụng migration ở chế độ “lai”:
    (1) Bạn kích hoạt chế độ automatic migration như trên.
    (2) Khi cần tạo/cập nhật CSDL, bạn nhập luôn lệnh update-database từ Package Manager Console như đối với manual migration.

    Cách thức này còn tiện lợi hơn cả. Bạn vừa kiểm soát được thời gian tạo CSDL, vừa không cần tự tay thêm file migration mới làm rối project.

    Kết luận

    Trong bài học này bạn đã nắm được cách làm việc với cơ chế migration trong entity framework. Cơ chế này cho phép bạn cập nhật cấu trúc cơ sở dữ liệu theo cấu trúc model class đồng thời hạn chế tối đa việc làm mất dữ liệu.

    Đây cũng là bài học thứ hai liên quan trực tiếp đến việc tạo và cập nhật cơ sở dữ liệu với code-first.

    Bắt đầu từ bài học sau bạn sẽ chuyển sang học cách cấu hình model class để EF có thể tạo CSDL chính xác theo yêu cầu của bạn.

    Nếu có thắc mắc hoặc cần trao đổi thêm, mời bạn viết trong phần Bình luận ở cuối trang. Nếu cần trao đổi riêng, hãy gửi email hoặc nhắn tin qua form liên hệ. Nếu bài viết hữu ích với bạn, hãy giúp chúng tôi chia sẻ tới mọi người. Cảm ơn bạn!

    Bình luận

    avatar
      Đăng ký theo dõi  
    Thông báo về