Chào mừng bạn trở lại với series “Lộ Trình học ASP.NET Core“! Trong các bài viết trước, chúng ta đã cùng nhau khám phá những nền tảng cốt lõi như ngôn ngữ C#, hệ sinh thái .NET, công cụ dòng lệnh .NET CLI, và quan trọng hơn cả là SQL và cơ sở dữ liệu quan hệ. Chúng ta đã hiểu về các khái niệm như bảng, khóa chính, khóa ngoại, và thậm chí cả Stored Procedures hay Constraints.
Tuy nhiên, việc thao tác với cơ sở dữ liệu trực tiếp bằng SQL trong ứng dụng .NET có thể khá lặp đi lặp lại và đôi khi phức tạp, đặc biệt khi cấu trúc dữ liệu thay đổi. Đây chính là lúc các công cụ ORM (Object-Relational Mapper) phát huy sức mạnh. Và trong thế giới .NET hiện đại, Entity Framework Core (EF Core) là lựa chọn hàng đầu.
Bài viết này sẽ đưa bạn đến với EF Core, tập trung vào phương pháp Code-First – cách tiếp cận được ưa chuộng nhất hiện nay, giúp bạn định nghĩa cấu trúc database ngay từ các class C# của mình. Hãy cùng bắt đầu nhé!
Mục lục
Entity Framework Core Là Gì? Tại Sao Nên Sử Dụng?
Entity Framework Core là một bộ công cụ ORM mã nguồn mở, nhẹ, có khả năng mở rộng và đa nền tảng, được phát triển bởi Microsoft. Vai trò chính của EF Core là làm “cầu nối” giữa mã C# (các đối tượng) và cơ sở dữ liệu quan hệ (các bảng).
Thay vì viết các chuỗi lệnh SQL thuần túy để thực hiện các thao tác CRUD (Create, Read, Update, Delete), bạn sẽ làm việc trực tiếp với các đối tượng C#. EF Core sẽ chịu trách nhiệm dịch các thao tác trên đối tượng của bạn thành các câu lệnh SQL tương ứng và thực thi chúng trên database.
Lợi ích khi sử dụng EF Core:
- Tăng tốc độ phát triển: Giảm đáng kể lượng mã boilerplate (mã lặp đi lặp lại) cần viết để tương tác với database.
- Giảm sự khác biệt giữa Object và Relational Worlds: Giúp lập trình viên tập trung vào logic nghiệp vụ bằng cách làm việc với các đối tượng quen thuộc.
- Hỗ trợ đa nền tảng database: Với các provider khác nhau, EF Core có thể làm việc với SQL Server, SQLite, PostgreSQL, MySQL, Oracle, v.v.
- Hỗ trợ LINQ: Cho phép viết các truy vấn dữ liệu mạnh mẽ và an toàn về kiểu (type-safe) bằng cú pháp C# quen thuộc.
- Quản lý schema database dễ dàng với Migrations: Cho phép tiến hóa cấu trúc database một cách có kiểm soát khi model C# thay đổi.
Code-First: Định Nghĩa Database Từ Mã Nguồn
EF Core hỗ trợ hai phương pháp chính để làm việc với database:
- Database-First: Bắt đầu từ database có sẵn, EF Core sẽ scaffold (tạo mã tự động) các class entity và DbContext tương ứng từ schema database.
- Code-First: Bắt đầu từ các class C# (được gọi là entity), bạn định nghĩa model dữ liệu của mình. EF Core sau đó sẽ tạo hoặc cập nhật schema database dựa trên các class này.
Trong bối cảnh phát triển ứng dụng hiện đại, đặc biệt là khi bắt đầu một dự án mới hoặc khi cấu trúc database thường xuyên thay đổi theo yêu cầu nghiệp vụ, phương pháp Code-First thường được ưa chuộng hơn. Nó giúp developer giữ cấu trúc database đồng bộ với mã nguồn, dễ dàng quản lý thay đổi bằng hệ thống quản lý phiên bản (như Git – bạn có thể xem lại bài viết về Quản lý mã nguồn với Git).
Với Code-First, bạn là người làm chủ model dữ liệu trong mã C#, và database sẽ “đi theo” model đó.
Thiết Lập Dự Án Bắt Đầu Với EF Core Code-First
Để bắt đầu, bạn cần có .NET SDK đã được cài đặt trên máy tính (Nếu chưa, hãy xem lại bài Tìm Hiểu Hệ Sinh Thái .NET: Runtime, SDK, và CLI). Chúng ta sẽ tạo một project Console đơn giản để minh họa.
Mở Terminal hoặc Command Prompt và chạy các lệnh sau:
dotnet new console -n EFCoreGettingStarted
cd EFCoreGettingStarted
Tiếp theo, chúng ta cần thêm các gói NuGet cần thiết. EF Core được phân phối dưới dạng các gói riêng lẻ. Gói cốt lõi là `Microsoft.EntityFrameworkCore`, và bạn cần thêm gói provider database tương ứng (ví dụ: `Microsoft.EntityFrameworkCore.SqlServer` cho SQL Server, `Microsoft.EntityFrameworkCore.Sqlite` cho SQLite, v.v.). Ngoài ra, bạn cũng cần công cụ dòng lệnh EF Core để quản lý migrations.
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
Sau khi thêm gói `Microsoft.EntityFrameworkCore.Tools`, bạn cũng nên cài đặt công cụ dòng lệnh `dotnet ef` global hoặc local cho project:
dotnet tool install --global dotnet-ef
Hoặc, nếu bạn muốn cài đặt local cho project:
dotnet new tool-manifest
dotnet tool install dotnet-ef
Lệnh cài đặt global tiện lợi hơn cho việc sử dụng chung, trong khi local tốt cho việc quản lý phiên bản công cụ theo project.
Định Nghĩa Model (Entities)
Trong Code-First, “model” của bạn chính là các class C# biểu diễn các bảng trong database. Các class này thường được gọi là “entity”. Hãy tạo một class đơn giản để biểu diễn một sản phẩm.
Tạo một file mới tên là `Product.cs` và thêm nội dung sau:
using System.ComponentModel.DataAnnotations;
public class Product
{
[Key] // Chỉ định Id là khóa chính
public int Id { get; set; }
[Required] // Chỉ định thuộc tính này là bắt buộc (NOT NULL trong DB)
public string Name { get; set; }
public decimal Price { get; set; }
}
Trong ví dụ này:
- Class `Product` sẽ ánh xạ tới một bảng có tên mặc định là `Products`.
- Thuộc tính `Id` sẽ là khóa chính của bảng (EF Core nhận diện theo quy ước đặt tên `Id` hoặc `[TênEntity]Id`). Attribute `[Key]` giúp làm rõ điều này.
- Thuộc tính `Name` sẽ là một cột kiểu chuỗi trong bảng. Attribute `[Required]` sẽ làm cho cột này không được phép NULL ở cấp độ database.
- Thuộc tính `Price` sẽ là một cột kiểu decimal.
Các Attributes như `[Key]` và `[Required]` là một cách để cấu hình model của bạn, được gọi là Data Annotations. Có một cách khác mạnh mẽ và linh hoạt hơn là Fluent API, chúng ta sẽ nói sơ qua sau.
Tạo DbContext – Cửa Ngõ Tới Database
`DbContext` là class quan trọng nhất trong EF Core. Nó đại diện cho một phiên làm việc với database và là nơi bạn định cấu hình model, thực hiện truy vấn và lưu thay đổi. Bạn cần tạo một class kế thừa từ `Microsoft.EntityFrameworkCore.DbContext`.
Tạo một file mới tên là `AppDbContext.cs`:
using Microsoft.EntityFrameworkCore;
public class AppDbContext : DbContext
{
// DbSet<Product> biểu diễn tập hợp các Product trong database
// Nó sẽ ánh xạ tới bảng "Products"
public DbSet<Product> Products { get; set; }
// Phương thức này được dùng để cấu hình DbContext,
// bao gồm chuỗi kết nối database.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// Để đơn giản cho ví dụ Console App, chúng ta cấu hình trực tiếp ở đây.
// TRONG ỨNG DỤNG THỰC TẾ (ví dụ: ASP.NET Core), bạn nên lấy chuỗi kết nối
// từ file cấu hình (appsettings.json) và cấu hình DbContext qua Dependency Injection.
// Sử dụng SQL Server LocalDB - rất tiện cho phát triển cục bộ.
// Đảm bảo rằng bạn đã cài đặt SQL Server LocalDB.
optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=EFCoreGettingStartedDb;Trusted_Connection=True;MultipleActiveResultSets=true");
}
// Bạn có thể thêm các cấu hình nâng cao hơn cho model ở đây
// bằng cách override phương thức OnModelCreating.
/*
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Cấu hình sử dụng Fluent API
// Ví dụ: Đảm bảo tên sản phẩm là duy nhất
modelBuilder.Entity<Product>()
.HasIndex(p => p.Name)
.IsUnique();
}
*/
}
Trong class `AppDbContext`:
- Thuộc tính `DbSet
` cho EF Core biết rằng chúng ta có một tập hợp các `Product` cần quản lý, và nó nên ánh xạ tới một bảng trong database. - Phương thức `OnConfiguring` được override để thiết lập cấu hình cho DbContext. Ở đây, chúng ta dùng phương thức mở rộng `UseSqlServer` từ gói `Microsoft.EntityFrameworkCore.SqlServer` để chỉ định rằng chúng ta sẽ làm việc với database SQL Server và cung cấp chuỗi kết nối.
- Chuỗi kết nối `Server=(localdb)\\mssqllocaldb;Database=EFCoreGettingStartedDb;Trusted_Connection=True;` kết nối tới một instance SQL Server LocalDB (thường có sẵn khi cài Visual Studio hoặc SQL Server Data Tools), tạo hoặc kết nối tới database tên là `EFCoreGettingStartedDb` sử dụng xác thực Windows. `MultipleActiveResultSets=true` đôi khi cần thiết cho các kịch bản phức tạp hơn.
- Bạn có thể override `OnModelCreating` để sử dụng Fluent API cho cấu hình chi tiết hơn (được comment trong ví dụ).
Lưu ý quan trọng: Việc đặt chuỗi kết nối trực tiếp trong mã nguồn như ví dụ `OnConfiguring` chỉ nên dùng cho các ứng dụng console đơn giản hoặc ví dụ minh họa. Trong ứng dụng web (như ASP.NET Core), chuỗi kết nối nên được lưu trong file cấu hình (appsettings.json
) và được nạp vào DbContext thông qua hệ thống Dependency Injection và cấu hình. Đây là một trong những điểm bạn sẽ học khi đi sâu hơn vào Lộ trình ASP.NET Core.
Áp Dụng Migrations: Tạo Database Schema
Sau khi đã định nghĩa model (`Product`) và DbContext (`AppDbContext`), chúng ta cần thông báo cho EF Core biết về model này và yêu cầu nó tạo ra script để tạo schema database tương ứng. Quá trình này được thực hiện thông qua Migrations.
Migrations giúp bạn quản lý các thay đổi về schema database theo thời gian. Mỗi khi bạn thay đổi model C# (thêm/xóa class, thêm/xóa thuộc tính, thay đổi kiểu dữ liệu, thêm quan hệ…), bạn sẽ tạo một migration mới. Migration này chứa đựng các đoạn mã C# mô tả các thao tác database cần thiết để chuyển đổi từ schema cũ sang schema mới (ví dụ: `CreateTable`, `AddColumn`, `DropTable`, v.v.).
Sử dụng công cụ dòng lệnh `dotnet ef` mà chúng ta đã cài đặt, chạy lệnh sau từ thư mục gốc của project:
dotnet ef migrations add InitialCreate
- `dotnet ef migrations add`: Yêu cầu EF Core tạo một migration mới.
- `InitialCreate`: Tên của migration. Bạn nên đặt tên có ý nghĩa để dễ theo dõi (ví dụ: `AddProductEntity`, `AddOrderAndRelationship`, v.v.).
Sau khi chạy lệnh này, EF Core sẽ:
- Scan project của bạn để tìm DbContext.
- Phân tích model được định nghĩa trong DbContext (class `Product`).
- So sánh model hiện tại với trạng thái cuối cùng được ghi nhận trong folder Migrations (Nếu chưa có migration nào, nó sẽ so sánh với trạng thái database trống).
- Tạo một folder `Migrations` trong project của bạn.
- Tạo hai file C# bên trong folder `Migrations`: một file chứa các thao tác `Up` (áp dụng migration) và `Down` (rollback migration), và một file snapshot của model hiện tại.
Kiểm tra folder `Migrations`, bạn sẽ thấy các file được tạo ra. File migration (ví dụ: `20231027100000_InitialCreate.cs` – phần số là timestamp) sẽ có code trông giống thế này:
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EFCoreGettingStarted.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Products",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"), // Cột tự tăng
Name = table.Column<string>(type: "nvarchar(max)", nullable: false), // NOT NULL do [Required]
Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Products", x => x.Id); // Khóa chính
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Products");
}
}
}
File này mô tả việc tạo bảng “Products” trong phương thức `Up` và xóa bảng đó trong phương thức `Down`. EF Core đã dịch các thuộc tính trong class `Product` thành các cột với kiểu dữ liệu và ràng buộc tương ứng.
Bước tiếp theo là áp dụng migration này vào database. Chạy lệnh sau:
dotnet ef database update
Lệnh này sẽ:
- Tìm DbContext.
- Đọc chuỗi kết nối từ DbContext.
- Kết nối tới database (nếu database chưa tồn tại, EF Core sẽ cố gắng tạo mới nó).
- Kiểm tra bảng lịch sử migrations trong database (`__EFMigrationsHistory`).
- Thực thi các migration chưa được áp dụng. Trong trường hợp này, nó sẽ chạy phương thức `Up` của migration `InitialCreate`, tạo bảng `Products`.
Bây giờ, bạn đã có một database với bảng `Products` được tạo ra dựa trên class `Product` của bạn!
Thực Hiện Các Thao Tác CRUD
Sau khi có database, chúng ta có thể sử dụng DbContext để thực hiện các thao tác Create, Read, Update, Delete dữ liệu.
Mở file `Program.cs` và thay thế nội dung bằng mã sau:
using System;
using System.Linq; // Cần thiết cho LINQ
public class Program
{
public static void Main(string[] args)
{
// Đảm bảo database được tạo (chỉ cần chạy migration một lần,
// nhưng lệnh update này cũng đảm bảo database tồn tại)
using (var context = new AppDbContext())
{
context.Database.EnsureCreated(); // Chỉ dùng cho ví dụ đơn giản/test. Migration tốt hơn cho production.
// Với Code-First & Migrations, bạn sẽ dùng:
// context.Database.Migrate();
}
Console.WriteLine("Database ensured/migrated.");
// --- CREATE ---
Console.WriteLine("\n--- Creating new products ---");
using (var context = new AppDbContext())
{
var product1 = new Product { Name = "Laptop", Price = 1200.50m };
var product2 = new Product { Name = "Keyboard", Price = 75.00m };
var product3 = new Product { Name = "Mouse", Price = 25.99m };
context.Products.Add(product1); // Thêm một đối tượng
context.Products.AddRange(product2, product3); // Thêm nhiều đối tượng
context.SaveChanges(); // Lưu thay đổi vào database
Console.WriteLine($"Added Product 1: {product1.Name} with Id {product1.Id}");
Console.WriteLine($"Added Product 2: {product2.Name} with Id {product2.Id}");
Console.WriteLine($"Added Product 3: {product3.Name} with Id {product3.Id}");
}
// --- READ ---
Console.WriteLine("\n--- Reading products ---");
using (var context = new AppDbContext())
{
// Lấy tất cả sản phẩm
Console.WriteLine("All products:");
var allProducts = context.Products.ToList(); // Chuyển truy vấn LINQ thành list và thực thi trên DB
foreach (var p in allProducts)
{
Console.WriteLine($"- {p.Id}: {p.Name} - {p.Price:C}"); // :C định dạng tiền tệ
}
// Lấy sản phẩm theo Id
int productIdToFind = 1;
Console.WriteLine($"\nFinding product with Id = {productIdToFind}:");
var productById = context.Products.Find(productIdToFind); // Find hiệu quả khi truy vấn bằng khóa chính
if (productById != null)
{
Console.WriteLine($"- Found: {productById.Name}");
}
else
{
Console.WriteLine($"- Product with Id {productIdToFind} not found.");
}
// Lấy sản phẩm theo điều kiện (sử dụng LINQ)
Console.WriteLine("\nProducts with price > 50:");
var expensiveProducts = context.Products
.Where(p => p.Price > 50) // Truy vấn LINQ
.OrderBy(p => p.Price) // Sắp xếp
.ToList(); // Thực thi truy vấn
foreach (var p in expensiveProducts)
{
Console.WriteLine($"- {p.Name}: {p.Price:C}");
}
}
// --- UPDATE ---
Console.WriteLine("\n--- Updating a product ---");
using (var context = new AppDbContext())
{
// Lấy sản phẩm cần cập nhật (ví dụ: sản phẩm có Id = 1)
var productToUpdate = context.Products.Find(1); // Find hoặc FirstOrDefault
if (productToUpdate != null)
{
Console.WriteLine($"Original Price of '{productToUpdate.Name}': {productToUpdate.Price:C}");
productToUpdate.Price = 1150.00m; // Thay đổi thuộc tính của đối tượng
context.SaveChanges(); // EF Core theo dõi thay đổi và tạo câu lệnh UPDATE
Console.WriteLine($"Updated Price to: {productToUpdate.Price:C}");
}
else
{
Console.WriteLine("Product with Id 1 not found for update.");
}
}
// --- DELETE ---
Console.WriteLine("\n--- Deleting a product ---");
using (var context = new AppDbContext())
{
// Lấy sản phẩm cần xóa (ví dụ: sản phẩm có Id = 3)
var productToDelete = context.Products.Find(3); // Find hoặc FirstOrDefault
if (productToDelete != null)
{
Console.WriteLine($"Deleting product: '{productToDelete.Name}'");
context.Products.Remove(productToDelete); // Đánh dấu đối tượng để xóa
context.SaveChanges(); // EF Core tạo câu lệnh DELETE
Console.WriteLine("Product deleted.");
}
else
{
Console.WriteLine("Product with Id 3 not found for deletion.");
}
}
// --- Verify DELETE ---
Console.WriteLine("\n--- Verifying deletion ---");
using (var context = new AppDbContext())
{
var remainingProducts = context.Products.ToList();
Console.WriteLine("Remaining products:");
if (remainingProducts.Any())
{
foreach (var p in remainingProducts)
{
Console.WriteLine($"- {p.Id}: {p.Name} - {p.Price:C}");
}
}
else
{
Console.WriteLine("No products left in the database.");
}
}
Console.WriteLine("\nDone.");
}
}
Chạy chương trình bằng lệnh:
dotnet run
Bạn sẽ thấy output hiển thị các bước tạo, đọc, cập nhật và xóa dữ liệu. Hãy phân tích code:
- Chúng ta sử dụng khối `using (var context = new AppDbContext())` để đảm bảo rằng DbContext được disposed (giải phóng tài nguyên) đúng cách sau khi sử dụng. DbContext là một đối tượng nhẹ nhưng kết nối tới database, nên việc giải phóng là cần thiết.
- `context.Database.EnsureCreated()` được dùng để tạo database nếu nó chưa tồn tại. Tuy nhiên, với Migrations, cách tốt hơn là sử dụng `context.Database.Migrate()`, lệnh này sẽ áp dụng tất cả các migrations chưa được áp dụng (bao gồm cả việc tạo database nếu nó chưa có).
- CREATE: Sử dụng `context.Products.Add()` hoặc `context.Products.AddRange()` để thêm một hoặc nhiều đối tượng `Product` vào bộ nhớ của DbContext. Các đối tượng này được đánh dấu là “Added”. Lệnh `context.SaveChanges()` sẽ thực hiện các thao tác INSERT vào database cho tất cả các đối tượng được đánh dấu thay đổi.
- READ: Sử dụng `context.Products` là một `DbSet
`, cho phép bạn truy vấn dữ liệu. - `context.Products.ToList()` lấy tất cả các bản ghi.
- `context.Products.Find(id)` tìm kiếm nhanh một bản ghi theo khóa chính.
- Bạn có thể sử dụng LINQ (Language Integrated Query) để viết các truy vấn phức tạp hơn như `Where`, `OrderBy`, `Select`, `GroupBy`, v.v. EF Core sẽ dịch các truy vấn LINQ này thành SQL và thực thi trên database. Lưu ý rằng truy vấn LINQ chỉ được thực thi trên database khi bạn gọi các phương thức như `ToList()`, `FirstOrDefault()`, `Count()`, v.g. trước đó, chúng chỉ là các biểu thức truy vấn.
- UPDATE: Lấy đối tượng từ database (bằng `Find`, `FirstOrDefault`, hoặc truy vấn khác). Thay đổi thuộc tính của đối tượng đó. Khi `context.SaveChanges()` được gọi, EF Core sẽ phát hiện những thay đổi trên đối tượng đã được theo dõi và tạo câu lệnh UPDATE tương ứng.
- DELETE: Lấy đối tượng từ database. Sử dụng `context.Products.Remove()` để đánh dấu đối tượng là cần xóa. Khi `context.SaveChanges()` được gọi, EF Core sẽ tạo câu lệnh DELETE tương ứng.
Việc sử dụng `async/await` với các phương thức bất đồng bộ như `AddAsync`, `ToListAsync`, `FirstOrDefaultAsync`, `SaveChangesAsync` là best practice trong các ứng dụng I/O-bound (như ứng dụng web, dịch vụ) để tránh blocking luồng. Trong ví dụ console đơn giản này, chúng ta dùng phương thức đồng bộ cho dễ hiểu, nhưng bạn nên chuyển sang bất đồng bộ khi phát triển ứng dụng thực tế.
Cấu Hình Model Nâng Cao (Sơ lược)
Ngoài Data Annotations, bạn có thể sử dụng Fluent API trong phương thức `OnModelCreating` của DbContext để cấu hình model chi tiết hơn. Fluent API cung cấp nhiều tùy chọn hơn và là cách tốt nhất để xử lý các cấu hình phức tạp hoặc khi bạn không muốn “làm bẩn” class entity bằng các attribute.
Ví dụ về cấu hình bằng Fluent API (đã comment trong file `AppDbContext.cs` ở trên):
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Cấu hình cho entity Product
modelBuilder.Entity<Product>(entity =>
{
// Đặt tên bảng là "ProductsInfo" thay vì tên mặc định "Products"
entity.ToTable("ProductsInfo");
// Cấu hình khóa chính (thường không cần nếu theo quy ước)
entity.HasKey(e => e.Id);
// Cấu hình thuộc tính Name:
// - Là bắt buộc
// - Có độ dài tối đa
// - Tạo Index duy nhất
entity.Property(e => e.Name)
.IsRequired()
.HasMaxLength(200); // Ví dụ thêm độ dài tối đa
entity.HasIndex(e => e.Name)
.IsUnique();
// Cấu hình thuộc tính Price:
// - Có độ chính xác và tỷ lệ thập phân cụ thể
entity.Property(e => e.Price)
.HasColumnType("decimal(10, 2)"); // Ví dụ: 10 chữ số tổng cộng, 2 chữ số sau thập phân
});
// Cấu hình mối quan hệ giữa các entities (sẽ học trong bài sau)
// modelBuilder.Entity<Order>()
// .HasMany(o => o.OrderItems)
// .WithOne(oi => oi.Order);
}
Sau khi thay đổi `OnModelCreating` (hoặc thêm Data Annotations mới), bạn cần tạo một migration mới và cập nhật database để áp dụng các thay đổi này.
dotnet ef migrations add ApplyAdvancedProductConfig
dotnet ef database update
Tóm tắt Các Lệnh dotnet ef Cơ Bản
Để giúp bạn dễ nhớ, đây là bảng tóm tắt các lệnh `dotnet ef` phổ biến nhất khi làm việc với Code-First Migrations:
Lệnh dotnet ef |
Mô tả |
---|---|
dotnet tool install --global dotnet-ef |
Cài đặt công cụ dòng lệnh EF Core global trên máy. |
dotnet ef migrations add [TênMigration] |
Tạo một migration mới dựa trên sự khác biệt giữa model hiện tại và snapshot migration cuối cùng. |
dotnet ef migrations list |
Liệt kê tất cả các migrations đã được tạo trong project. |
dotnet ef database update |
Áp dụng tất cả các migrations chưa được áp dụng vào database đích. |
dotnet ef database update [TênMigration] |
Áp dụng hoặc rollback database đến một migration cụ thể. Dùng 0 để rollback về trạng thái database trống. |
dotnet ef database drop |
Xóa database đích (cẩn thận khi sử dụng!). |
dotnet ef migrations remove |
Xóa migration cuối cùng đã được tạo (trước khi áp dụng vào database). |
Kết Luận
Chúc mừng! Bạn đã hoàn thành bài giới thiệu về Entity Framework Core với phương pháp Code-First. Bạn đã học cách:
- Hiểu vai trò của EF Core như một ORM.
- Áp dụng phương pháp Code-First để định nghĩa schema database từ mã C#.
- Thiết lập project .NET với các gói NuGet cần thiết.
- Tạo các class Entity và DbContext.
- Sử dụng Migrations để tạo và cập nhật schema database.
- Thực hiện các thao tác CRUD cơ bản (Create, Read, Update, Delete) trên dữ liệu sử dụng DbContext và LINQ.
- Nắm sơ lược về cách cấu hình model bằng Data Annotations và Fluent API.
Đây chỉ là những bước chân đầu tiên trên con đường khám phá EF Core. Vẫn còn rất nhiều điều thú vị và quan trọng phía trước như: xử lý các mối quan hệ giữa các entities (One-to-Many, Many-to-Many – rất quan trọng trong database quan hệ!), tối ưu hóa truy vấn, xử lý giao dịch (transactions), sử dụng Asynchronous API, testing với EF Core, v.v.
Hãy thực hành thật nhiều với ví dụ này và thử mở rộng nó. Thêm các entity khác, tạo mối quan hệ đơn giản, thử nghiệm các truy vấn LINQ khác nhau. Việc thực hành là chìa khóa để làm chủ EF Core.
Trong bài viết tiếp theo của series Lộ Trình .NET, chúng ta sẽ đi sâu hơn vào cách xử lý các mối quan hệ giữa các bảng/entities trong EF Core, một kỹ năng cực kỳ quan trọng khi làm việc với database quan hệ. Đừng bỏ lỡ nhé!
Cảm ơn bạn đã đồng hành và hẹn gặp lại trong các bài viết tiếp theo!