[Entity Framework] Phần 3: Viết mã nguồn trước, tạo mới cơ sở dữ liệu sau (Code First to a New Database)

600 lượt xem

Khi phát triển 1 ứng dụng phần mềm nói chung, chắc hẳn bạn đã quen với việc khảo sát hiện trang, phân tích yêu cầu đầu vào, vẽ sơ đồ Use Case, sơ đồ lớp, … rồi sau đó là thiết kế cơ sở dữ liệu trước khi bắt tay vào lập trình xây dựng chương trình. Tuy nhiên, đây là cách tiếp cận truyền thống và trong 1 số trường hợp có thể không phù hợp.

Một cách khác, có thể chúng ta thường hay làm mà ít để ý đó là thiết kế mã nguồn chương trình trước, sau đó gieo mới cơ sở dữ liệu sau. Nội dung bài này sẽ trình bày về vấn đề này với việc sử dụng Entity Framework.

Các điều kiện cần phải có
Để thực hiện cách phát triển phần mềm này, bạn cần cài đặt đầy đủ phiên bản Visual Studio 2010 hoặc Visual Studio 2012. Nếu là Visual Studio 2010 thì cài thêm gói NuGet để cài thêm Entity Framework. Admin hiện nay đang dùng Visual Studio 2013 được tích hợp sẵn Entity Framework.

1. Tạo ứng dụng
Đầu tiên, chúng ta tạo 1 ứng dụng mới như sau:

  • Mở Visual Studio, chọn File -> New -> Project …, sau đó chọn Visual C# ở phía cột trái và chọn Console Application, đặt tên ứng dụng là DammioCodeFirstNewDatabase và chọn OK.

2. Tạo mô hình
Ở bước này, chúng ta sẽ định nghĩa mô hình cho dự án bằng cách thêm các lớp mã nguồn ở tập tin Program.cs. Bạn có thể tách mỗi lớp thành 1 tập tin riêng nếu muốn.

Ví dụ sau mô tả tập tin Program.cs chứa hai lớp BlogPost. Một Blog chứa nhiều Post (bài viết) và 1 Post (bài viết) chỉ thuộc về 1 Blog duy nhất. Lớp Blog chứa 2 thuộc tính BlogId và Name, và thuộc tính ảo điều hướng Posts liệt kê các bài viết thuộc 1 Blog. Lớp Post chứa 3 thuộc tính PostId, Title, Content và thuộc tính khóa ngoại BlogId, cũng như thuộc tính điều hướng ảo Blog để cho biết 1 Post thuộc về duy nhất 1 Blog nào.

public class Blog 
{ 
    public int BlogId { get; set; } 
    public string Name { get; set; } 

    public virtual List<Post> Posts { get; set; } 
} 
 
public class Post 
{ 
    public int PostId { get; set; } 
    public string Title { get; set; } 
    public string Content { get; set; } 
 
    public int BlogId { get; set; } 
    public virtual Blog Blog { get; set; } 
}

Trong ví dụ trên, chúng ta tạo hai thuộc tính điều hướng (Blog.Posts và Post.Blog) ảo. Điều này cho phép tính năng Lazy Loading (tải lười hoặc tải sau) của Entity Framework. Lazy Loading nghĩa là nội dung của các thuộc tính sẽ tự động được tải lên từ database khi bạn truy xuất chúng. Còn nếu bạn không có yêu cầu truy xuất, các thuộc tính này sẽ không được tự động tải.

3. Tạo bối cảnh (context)
Bạn phải định nghĩa 1 bối cảnh, thể hiện 1 phiên làm việc với database, cho phép thực thi truy vấn và lưu trữ dữ liệu. Chúng ta xây dựng 1 bối cảnh kế thừa từ System.Data.Entity.DbContext và biểu diễn dạng DbSet cho mỗi lớp trong mô hình.

Để dùng các dạng của Entity Framework, cần cài đặt gói EntityFramework NuGet. Để cài đặt, chọn Tools -> Library Package Manage -> Manage NuGet Packages for Solution…, chọn nuget.org, chọn gói EntityFramework và nhấn Install để cài. Sau đó, thêm using System.Data.Entity; ở đầu tập tin Program.cs.

Sau đó thêm đoạn mã sau vào tập tin Program.cs

public class BloggingContext : DbContext 
{ 
    public DbSet<Blog> Blogs { get; set; } 
    public DbSet<Post> Posts { get; set; } 
}

Đây là nội dung đầy đủ của tập tin Program.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Data.Entity; 
 
namespace CodeFirstNewDatabaseSample 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
        } 
    } 
 
    public class Blog 
    { 
        public int BlogId { get; set; } 
        public string Name { get; set; } 
 
        public virtual List<Post> Posts { get; set; } 
    } 
 
    public class Post 
    { 
        public int PostId { get; set; } 
        public string Title { get; set; } 
        public string Content { get; set; } 
 
        public int BlogId { get; set; } 
        public virtual Blog Blog { get; set; } 
    } 
 
    public class BloggingContext : DbContext 
    { 
        public DbSet<Blog> Blogs { get; set; } 
        public DbSet<Post> Posts { get; set; } 
    } 
}

4. Đọc & Ghi dữ liệu
Để đọc và ghi dữ liệu, bạn có thể thực thi ở phương thức Main trong tập tin Program.cs. Đoạn mã sau tạo mới 1 Blog và lấy toàn bộ Blog sắp xếp theo Name trong database bằng LINQ để xuất kết quả ra màn hình.

class Program 
{ 
    static void Main(string[] args) 
    { 
        using (var db = new BloggingContext()) 
        { 
            // Nhập tên 1 Blog mới cần tạo
            Console.Write("Ten cua 1 Blog moi: "); 
            var name = Console.ReadLine(); 
 
            // Thêm 1 Blog mới	
            var blog = new Blog { Name = name }; 
            db.Blogs.Add(blog); 
            db.SaveChanges(); 
 
            // Hiển thị các Blog trong database, sắp xếp theo Name
            var query = from b in db.Blogs 
                        orderby b.Name 
                        select b; 
 
            Console.WriteLine("Tat ca blog trong database -- dammio.com:"); 
            foreach (var item in query) 
            { 
                Console.WriteLine(item.Name); 
            } 
 
            Console.WriteLine("Go bat ky phim nao de thoat..."); 
            Console.ReadKey(); 
        } 
    } 
}

Ok, đến bước này, bạn đã thêm được 1 Blog mới và xuất hiện kết quả các Blog vừa thêm ra màn hình. Sẽ rất nhiều bạn thắc mắc dữ liệu của ứng dụng ở đâu. Visual Studio sẽ tự động tạo database và thường là ở SQL Express (Visual Studio 2010) hoặc là LocalDb (ở Visual 2012). Tên database được đặt theo tên dự án và tên bối cảnh, đó là DammioCodeFirstNewDatabase.BloggingContext.

Có 1 số cách kết nối với database, bạn thử 1 trong các cách sau. Trong Server Explorer ở Visual Studio, chọn View -> Server Explorer. Chuột phải chọn Data Connections và chọn Add Connection… Tiếp theo gõ (localdb)\v11.0 hoặc .\SQLEXPRESS vào phần Server Name, sau đó chọn database của bạn.

DbContext làm việc với các lớp trong mô hình bằng cách nhìn các thuộc tính DbSet mà chúng ta đã định nghĩa. Sau đó dùng tập mặc định các quy ước Code First để xác định bảng và tên các cột, xác định kiểu dữ liệu, tìm các khóa chính, …

5. Giải quyết vấn đề thay đổi mô hình
Cái hay nhất của Code First là cho phép thay đổi mô hình code, từ đó thay đổi luôn database tùy ý. Để làm được việc này, chúng ta sẽ áp dụng 1 tính năng gọi là Code First Migrations (dịch nôm na là tích hợp Code First), hay gọi là Migrations cho ngắn gọn.

Migrations cho phép thiết lập 1 tập các bước mô hình cách nâng cấp/bỏ bớt lược đồ database. Ở mỗi bước, được xem là 1 migration, chứa đoạn mã mô tả các thay đổi được áp dụng.

Bước đầu tiên là kích hoạt chế độ Code First Migration cho BloggingContext bằng cách vào Toosl -> Library Package Manager -> Package Manager Console, gõ dòng lệnh Enable-Migrations và nhấn Enter như hình.

Sau đó, bạn có thể thấy thư mục Migrations như trong hình sau.

Tiếp theo chúng ta thử thay đổi mô hình, thêm thuộc tính Url vào lớp Blog như sau:

public class Blog 
{ 
    public int BlogId { get; set; } 
    public string Name { get; set; } 
    public string Url { get; set; } // thêm thuộc tính mới --- dammio.com
 
    public virtual List<Post> Posts { get; set; } 
}

Sau đó chạy dòng lệnh Add-Migration AddUrl với AddUrl là tham số tên, thật ra bạn đặt bất cứ tên gì cũng được. Khi chạy dòng lệnh trên, Migrations sẽ kiểm tra lần tích hợp cuối của bạn và xây dựng 1 mô hình mới với bất cứ thay đổi được tìm thấy, sau đó sẽ thay đổi database cho phù hợp với mô hình này. Thậm chí, bạn có thể vào lớp AddUrl vừa tạo để chỉnh tay thêm/xóa các trường cần thiết.

namespace DammioCodeFirstNewDatabase.Migrations
{
    using System;
    using System.Data.Entity.Migrations;
    
    public partial class AddUrl : DbMigration
    {
        public override void Up()
        {
            AddColumn("dbo.Blogs", "Url", c => c.String());
        }
        
        public override void Down()
        {
            DropColumn("dbo.Blogs", "Url");
        }
    }
}

Sau đó, ở Package Manager Console, tiếp tục chạy dòng lệnh Update-Database để cập nhật mô hình mới trong database. Trong database, bảng Blog sẽ có thêm cột Url mới.

6. Chú thích dữ liệu (Data Annotations)
Phần này chỉ các bạn cách chú thích dữ liệu cho các thuộc tính ở lớp trong mô hình. Có 2 cách chính là dùng Data Annotations hay Fluent API ở phần tiếp theo. Tiếp theo chúng ta thêm 1 lớp mới User vào mô hình ở tập tin Program.cs.

public class User 
{ 
    public string Username { get; set; } 
    public string DisplayName { get; set; } 
}

Chúng ta cũng cần thêm 1 tập vào bối cảnh kế thừa.

public class BloggingContext : DbContext 
{ 
    public DbSet<Blog> Blogs { get; set; } 
    public DbSet<Post> Posts { get; set; } 
    public DbSet<User> Users { get; set; }  // thêm mới DbSet User
}

Khi chúng ta tích hợp như phần 5 thì bị gặp vấn đề đó là thực thể “User” không có khóa chính. Vì vậy chúng ta phải dùng Data Annotations để định nghĩa khóa chính cho User, trước hết thì nhúng thêm dòng vào đầu tập tin Program.cs.

using System.ComponentModel.DataAnnotations;

Sau đó, chú thích thuộc tính Username là khóa chính với cú pháp [Key]

public class User 
{ 
    [Key] // đây là chú thích dữ liệu có ý nghĩa thuộc tính Username là khóa chính của lớp
    public string Username { get; set; } 
    public string DisplayName { get; set; } 
}

Dùng lệnh Add-Migration AddUser và Update-Database trong Package Manager Console để cập nhật mô hình mới. Danh sách đầy đủ chú thích được hỗ trợ bởi EF là: KeyAttribute, StringLengthAttribute, MaxLengthAttribute, ConcurrencyCheckAttribute, RequiredAttribute, TimestampAttribute, ComplexTypeAttribute, ColumnAttribute, TableAttribute, InversePropertyAttribute, ForeignKeyAttribute, DatabaseGeneratedAttribute, NotMappedAttribute.

7. Fluent API
Trong phần trước, chúng ta sử dụng Data Annotations để bổ sung hoặc ghi đè các thay đổi. Phần này chúng ta dùng Fluent API, một cách khác để làm việc đó. Đa số cấu hình mô hình được thực hiện dùng các chú thích dữ liệu đơn giản. Fluent API lại cho phép cách tốt hơn để đặc tả cấu hình mô hình bao gồm mọi thứ mà chú thích dữ liệu cho thể làm trong việc thêm các cấu hình nâng cao. Data Annotations và Fluent API có thể sử dụng cùng nhau.

Để dùng Fluent API, bạn có thể ghi đè (override) lên phương thức OnModelCreatingDbContext. Để thay tên cột User.DisplayName thành display_name, chúng ta có thể thực hiện như đoạn code sau.

public class BloggingContext : DbContext 
{ 
    public DbSet<Blog> Blogs { get; set; } 
    public DbSet<Post> Posts { get; set; } 
    public DbSet<User> Users { get; set; } 
 
    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
        modelBuilder.Entity<User>() 
            .Property(u => u.DisplayName) 
            .HasColumnName("display_name"); // thay đổi tên thuộc tính  --- dammio.com
    } 
}

Sau đó cũng chạy lệnh Add-Migration ChangeDisplayName và lệnh Update-Database để thay đổi mô hình.

Kết luận: Đến đây, bạn đã học cách tạo 1 ứng dụng theo phương pháp tạo mã nguồn trước, gieo mới database sau (Code First to a New Database). Tiếp đến, bạn cũng nắm cách tạo và lưu trữ dữ liệu trong database và cách dùng Code First Migrations để thay đổi mô hình, và cấu hình mô hình thông Data Annotations và Fluent API.

Bình luận Facebook

Để lại bình luận

1 Comment on "[Entity Framework] Phần 3: Viết mã nguồn trước, tạo mới cơ sở dữ liệu sau (Code First to a New Database)"

Thông báo khi có
avatar
1000
Sắp xếp theo:   mới nhất | cũ nhất | nhiều bình chọn nhất
tien huy
Khách

cách này chỉ tốt cho dự án và làm kiểu Agile thôi, chứ làm lớn thay code kha phuc tap

wpDiscuz