Guide: Working with Migrations
Relevant source files
The following files were used as context for generating this wiki page:- Migrations/UserDBContextModelSnapshot.cs
- Data/UserDBContextFactory.cs
- Data/UserDBContext.cs
- Migrations/20250104092740_New.cs
- Migrations/20250108150417_AddResetTokenToUser.cs
- Configuration/UserConfiguration.cs
- Configuration/ProductConfiguration.cs
- Configuration/OrderConfiguration.cs
- Seeders/DatabaseSeeder.cs
- Program.cs
This guide provides a comprehensive overview of how Entity Framework Core (EF Core) migrations are utilized within the WEBAPI project to manage and version the database schema. It covers the core components, the process of creating and applying migrations, and the resulting database structure. The system relies on a code-first approach, where C# model and configuration classes define the database schema, and migrations translate those definitions into executable SQL scripts.
The key components involved are the UserDBContext, entity configuration classes that implement IEntityTypeConfiguration<T>, individual migration files that represent incremental schema changes, and a model snapshot that provides a complete picture of the current database model.
Core Components
The migration system is built upon several key classes that work together to define, create, and maintain the database schema.
UserDBContext
The UserDBContext class is the central piece of the data access layer, acting as the bridge between the application's entity models and the database. It is responsible for applying all entity configurations and seeding initial data when the model is being created.
// Data/UserDBContext.cs:13-20
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Apply all configurations
modelBuilder.ApplyConfiguration(new UserConfiguration());
modelBuilder.ApplyConfiguration(new OrderConfiguration());
modelBuilder.ApplyConfiguration(new ProductConfiguration());
modelBuilder.ApplyConfiguration(new AdminConfiguration());
modelBuilder.ApplyConfiguration(new CategoryConfiguration()); // Add this line
modelBuilder.ApplyConfiguration(new OrderItemConfiguration());
// ...
}
The DbSet<T> properties on this context represent the tables in the database.
Sources: [Data/UserDBContext.cs:5-39](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Data/UserDBContext.cs#L5-L39)
UserDBContextFactory
For design-time operations, such as creating a new migration from the command line, EF Core needs a way to instantiate the UserDBContext. The UserDBContextFactory class serves this purpose. It reads the database connection string from appsettings.json and configures the DbContextOptions to connect to the SQL Server database.
The following diagram illustrates the flow for creating a DbContext instance at design time.
This diagram shows the design-time DbContext creation process.
Sources: [Data/UserDBContextFactory.cs:7-19](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Data/UserDBContextFactory.cs#L7-L19)
Entity Configurations
The schema for each entity is defined in a dedicated configuration class that implements IEntityTypeConfiguration<T>. This approach keeps the model classes clean and centralizes the database mapping logic. These classes define table names, primary keys, column types, lengths, and relationships.
For example, the UserConfiguration class configures the User entity:
// Configuration/UserConfiguration.cs:9-29
public void Configure(EntityTypeBuilder<User> builder)
{
// Đặt tên bảng
builder.ToTable("User");
// Định nghĩa khóa chính
builder.HasKey(u => u.Id);
builder.Property(u => u.Email)
.IsRequired()
.HasMaxLength(100); // Giới hạn độ dài chuỗi Email
builder.Property(u => u.UserName)
.IsRequired()
.HasMaxLength(50); // Giới hạn độ dài chuỗi UserName
//...
}
This pattern is repeated for all entities, including Product, Order, Admin, and Category.
Sources: [Configuration/UserConfiguration.cs:7-31](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Configuration/UserConfiguration.cs#L7-L31), [Configuration/ProductConfiguration.cs:8-40](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Configuration/ProductConfiguration.cs#L8-L40), [Configuration/OrderConfiguration.cs:8-29](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Configuration/OrderConfiguration.cs#L8-L29), [Configuration/AdminConfiguration.cs:7-28](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Configuration/AdminConfiguration.cs#L7-L28), [Configuration/CategoryConfiguration.cs:7-23](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Configuration/CategoryConfiguration.cs#L7-L23)
Service Registration
At runtime, the UserDBContext is registered with the application's dependency injection container in Program.cs. This allows services and controllers to receive a UserDBContext instance through their constructors.
// Program.cs:19-22
builder.Services.AddDbContext<UserDBContext>(option =>
{
option.UseSqlServer(builder.Configuration.GetConnectionString("UserDataBase"));
});
Sources: [Program.cs:19-22](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Program.cs#L19-L22)
The Migration Process
Migrations are the mechanism for evolving the database schema over time in a consistent and version-controlled manner.
Generating a Migration
A migration is a C# class that represents a set of schema changes. These are generated using EF Core tools (e.g., dotnet ef migrations add <MigrationName>). The tool compares the current model (defined by configurations and the snapshot) with the previous state to generate Up() and Down() methods.
For example, the AddResetTokenToUser migration adds two new nullable columns to the User table.
// Migrations/20250108150417_AddResetTokenToUser.cs:12-21
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "ResetToken",
table: "User",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "ResetTokenExpiration",
table: "User",
type: "datetime2",
nullable: true);
}
Sources: [Migrations/20250108150417_AddResetTokenToUser.cs:11-35](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Migrations/20250108150417_AddResetTokenToUser.cs#L11-L35)
Migration File Structure
Each migration file contains two key methods:
Up(MigrationBuilder migrationBuilder): This method applies the schema changes. It contains instructions to create tables, add columns, define constraints, etc.Down(MigrationBuilder migrationBuilder): This method reverts the changes made by theUpmethod. It is used when rolling back a migration.
The New migration file demonstrates the creation of multiple tables and their foreign key relationships.
// Migrations/20250104092740_New.cs:13-113
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Admins",
// ... columns
);
migrationBuilder.CreateTable(
name: "Categories",
// ... columns
);
migrationBuilder.CreateTable(
name: "User",
// ... columns
);
migrationBuilder.CreateTable(
name: "Product",
// ... columns and foreign key
);
migrationBuilder.CreateTable(
name: "Orders",
// ... columns and foreign keys
);
}
Sources: [Migrations/20250104092740_New.cs:11-149](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Migrations/20250104092740_New.cs#L11-L149)
The Model Snapshot
The UserDBContextModelSnapshot.cs file is an auto-generated file that represents the complete, current state of the database model as understood by EF Core. It is not a migration itself but is used as a source for creating the next migration. It provides a detailed definition of every table, column, key, and relationship.
Sources: [Migrations/UserDBContextModelSnapshot.cs](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Migrations/UserDBContextModelSnapshot.cs)
Database Schema and Relationships
The migrations and configurations define a clear database schema with relationships between entities.
The following Entity-Relationship diagram visualizes the tables and their connections.
This ER diagram shows the relationships between the main entities in the database.
Sources: [Migrations/UserDBContextModelSnapshot.cs:22-263](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Migrations/UserDBContextModelSnapshot.cs#L22-L263)
Key Entity Tables
User Table
| Column | Type | Constraints |
|---|---|---|
| Id | int | Primary Key, Identity(1,1) |
nvarchar(100) | Required | |
| UserName | nvarchar(50) | Required |
| Password | nvarchar(256) | Required |
| Role | bit | Required |
| ResetToken | nvarchar(max) | Nullable |
| ResetTokenExpiration | datetime2 | Nullable |
Sources: [Migrations/UserDBContextModelSnapshot.cs:202-231](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Migrations/UserDBContextModelSnapshot.cs#L202-L231), [Configuration/UserConfiguration.cs:18-29](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Configuration/UserConfiguration.cs#L18-L29)
Product Table
| Column | Type | Constraints |
|---|---|---|
| Id | int | Primary Key, Identity(1,1) |
| Title | nvarchar(100) | Required |
| Name | nvarchar(200) | Required |
| Description | nvarchar(500) | Required |
| Price | decimal(18,2) | Required |
| ProductImageUrl | nvarchar(500) | Required |
| CategoryId | int | Nullable, Foreign Key to Categories |
Sources: [Migrations/UserDBContextModelSnapshot.cs:169-200](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Migrations/UserDBContextModelSnapshot.cs#L169-L200), [Configuration/ProductConfiguration.cs:15-37](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Configuration/ProductConfiguration.cs#L15-L37)
Order Table
| Column | Type | Constraints |
|---|---|---|
| OrderID | int | Primary Key, Identity(1,1) |
| CustomerID | int | Required, Foreign Key to User |
| TotalAmount | decimal(18,2) | Required |
| OrderDate | datetime2 | Required, Default GETDATE() |
| Status | nvarchar(50) | Required |
| Address | nvarchar(200) | Required |
Sources: [Migrations/UserDBContextModelSnapshot.cs:86-126](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Migrations/UserDBContextModelSnapshot.cs#L86-L126), [Configuration/OrderConfiguration.cs:15-23](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Configuration/OrderConfiguration.cs#L15-L23)
Data Seeding
The application seeds initial data into the database using an extension method on ModelBuilder. The DatabaseSeeder.Seed() method is called within UserDBContext.OnModelCreating to ensure the data is present after migrations are applied.
Currently, it seeds a single default user.
// Seeders/DatabaseSeeder.cs:9-20
public static void Seed(this ModelBuilder modelBuilder)
{
_ = modelBuilder.Entity<User>().HasData(
new User()
{
Id = 1,
Email = "chihtt123@gmail.com",
UserName = "chi",
Password = "123",
Role = true
}
);
}
This seeder is invoked from the DbContext.
Sources: [Seeders/DatabaseSeeder.cs:7-22](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Seeders/DatabaseSeeder.cs#L7-L22), [Data/UserDBContext.cs:33-34](https://github.com/NguyenPhamQuocHuy-20130283/WEBAPI/blob/HEAD/Data/UserDBContext.cs#L33-L34)
Conclusion
The WEBAPI project employs a robust, code-first migration strategy using Entity Framework Core. This approach provides a version-controlled history of all database schema changes, ensures consistency across different environments, and simplifies database development by keeping the schema definition within the C# codebase. By separating concerns with DbContext, entity configurations, and seeder classes, the system remains organized and maintainable. Developers can confidently evolve the database schema by creating new migrations that reflect changes in the application's models.