Entity Framework Core 6 (EF Core 6) là phiên bản hiện đại, hiệu năng cao của ORM cho nền tảng .NET 6, giúp ánh xạ tự động giữa các lớp đối tượng C# và bảng cơ sở dữ liệu — giảm đáng kể sự phụ thuộc vào thao tác SQL thủ công và tăng tốc độ phát triển ứng dụng.
1. Thiết lập môi trường cơ bản
Để bắt đầu, cần cài đặt SQL Server cục bộ (hoặc sử dụng SQL Server Express / LocalDB), sau đó thêm các package NuGet cần thiết:
Microsoft.EntityFrameworkCore.SqlServer— trình điều khiển kết nối SQL ServerMicrosoft.EntityFrameworkCore.Tools— hỗ trợ lệnh migration trong Package Manager ConsoleMicrosoft.EntityFrameworkCore.Design— yêu cầu khi chạy migration từ dự án không phải là startup project (ví dụ: Console App)
2. Định nghĩa thực thể (Entity)
Hai lớp mẫu Employee và Organization được dùng để minh họa:
namespace DomainModels;
public class Employee
{
public int Id { get; set; }
public string FullName { get; set; } = null!;
public string? ContactEmail { get; set; }
public DateTime? HireDate { get; set; }
}
public class Organization
{
public int Id { get; set; }
public string Title { get; set; } = null!;
public string? Description { get; set; }
}3. Xây dựng DbContext
Lớp AppDbContext quản lý kết nối, cấu hình mô hình và cung cấp các DbSet:
using Microsoft.EntityFrameworkCore;
using DomainModels;
public class AppDbContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Organization> Organizations { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer(
"Data Source=.;Initial Catalog=DemoDB;Integrated Security=True;TrustServerCertificate=true");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(typeof(Employee).Assembly);
}
}4. Cấu hình mô hình bằng Fluent API
Thay vì dùng Data Annotations, ta tạo lớp cấu hình riêng để tăng tính linh hoạt và dễ bảo trì:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using DomainModels;
public class EmployeeConfiguration : IEntityTypeConfiguration<Employee>
{
public void Configure(EntityTypeBuilder<Employee> builder)
{
builder.ToTable("Employees");
builder.HasKey(e => e.Id);
builder.Property(e => e.FullName).IsRequired().HasMaxLength(120);
builder.Property(e => e.ContactEmail).HasMaxLength(254);
builder.HasIndex(e => e.ContactEmail).IsUnique();
}
}
public class OrganizationConfiguration : IEntityTypeConfiguration<Organization>
{
public void Configure(EntityTypeBuilder<Organization> builder)
{
builder.ToTable("Organizations");
builder.HasKey(o => o.Id);
builder.Property(o => o.Title).IsRequired().HasMaxLength(100);
}
}5. Thực hiện migration
Sau khi định nghĩa xong mô hình, mở Package Manager Console, chọn đúng project chứa AppDbContext, rồi chạy:
Add-Migration InitialSetup -Project DomainModels
Update-Database -Project DomainModelsMột số lệnh hữu ích khác:
Update-Database -Migration LastGoodState— quay lại trạng thái migration cụ thểRemove-Migration— hủy bỏ migration mới nhất chưa áp dụngScript-Migration -From 0 -To Latest— sinh script SQL để triển khai thủ công trên môi trường production
6. Thêm dữ liệu mẫu qua Console App
Tạo một ứng dụng console đơn giản để kiểm tra thao tác:
using DomainModels;
using var context = new AppDbContext();
context.Employees.Add(new Employee
{
FullName = "Nguyễn Văn A",
ContactEmail = "nva@example.com",
HireDate = DateTime.Today.AddDays(-10)
});
context.Organizations.Add(new Organization
{
Title = "Phòng Kỹ Thuật",
Description = "Quản lý phát triển phần mềm nội bộ"
});
context.SaveChanges();7. Bật ghi log truy vấn SQL
Thêm thư viện logging và cấu hình trong AppDbContext:
using Microsoft.Extensions.Logging;
public class AppDbContext : DbContext
{
private static readonly ILoggerFactory Logger = LoggerFactory.Create(b => b.AddConsole());
// ... phần còn lại như trên
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer(connectionString)
.UseLoggerFactory(Logger); // bật log SQL
}Kết quả sẽ hiển thị đầy đủ câu lệnh SQL được EF Core sinh ra trước khi thực thi.
8. Mối quan hệ thông qua Navigation Property
Giả sử mỗi nhân viên thuộc về một phòng ban (Department), và mỗi phòng ban có nhiều nhân viên:
public class Department
{
public int Id { get; set; }
public string Name { get; set; } = null!;
public ICollection<Employee> StaffMembers { get; set; } = new List<Employee>();
}
// Trong Employee:
public class Employee
{
// ... các thuộc tính khác
public int DepartmentId { get; set; }
public Department Department { get; set; } = null!;
}Cập nhật cấu hình Fluent:
builder.HasOne(e => e.Department)
.WithMany(d => d.StaffMembers)
.HasForeignKey(e => e.DepartmentId)
.OnDelete(DeleteBehavior.Cascade);Truy vấn dữ liệu kèm quan hệ:
var employeeWithDept = context.Employees
.Include(e => e.Department)
.FirstOrDefault(e => e.Id == 5);
Console.WriteLine($"{employeeWithDept.FullName} làm việc tại {employeeWithDept.Department.Name}");9. Sự khác biệt giữa IQueryable<T> và IEnumerable<T>
EF Core trả về IQueryable<T> cho các truy vấn — cho phép xây dựng biểu thức LINQ mà chưa thực thi ngay. Việc truy vấn chỉ xảy ra khi gọi phương thức "kết thúc" như ToList(), First(), hoặc khi duyệt qua bằng foreach.
IQueryable<Employee> query = context.Employees.Where(e => e.HireDate > DateTime.Now.AddYears(-2));
// Chưa truy vấn CSDL ở đây
var recentHires = query.ToList(); // ← Lúc này mới thực thi SELECTNgược lại, IEnumerable<T> thực hiện toàn bộ xử lý trên bộ nhớ sau khi đã tải dữ liệu — không tối ưu nếu tập dữ liệu lớn.
10. Quản lý giao dịch (Transaction)
Dùng TransactionScope để đảm bảo tính toàn vẹn dữ liệu khi thực hiện nhiều thao tác liên quan:
using var scope = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted },
TransactionScopeOption.Required);
try
{
context.Employees.Add(new Employee { FullName = "Lê Thị B", HireDate = DateTime.Today });
context.SaveChanges();
context.Organizations.Add(new Organization { Title = "Phòng Nhân Sự" });
context.SaveChanges();
scope.Complete(); // Chỉ khi gọi Complete thì mới commit
}
catch
{
// Tự động rollback nếu có ngoại lệ
}Với mã bất đồng bộ, sử dụng TransactionScopeAsyncFlowOption.Enabled để duy trì ngữ cảnh giao dịch qua các luồng await.