Entity Framework Core - Code First Approach
1. What is Code First?
In the Code First approach, you write your C# classes (Entities) first, and EF Core creates the database tables based on those classes. Your code is the "Source of Truth."
If you change your C# class (e.g., add a property), you run a Migration, and EF Core updates the database schema to match.
2. The Workflow
Define Models: Create C# classes (
User,Product).Configure Context: Create a
DbContextclass to manage these models.Add Migration: Generate a script (C# code) that describes how to create the DB.
Update Database: Apply that script to the actual SQL database.
3. Step-by-Step Implementation
Step A: Install NuGet Packages
You need the EF Core tools and the provider for your specific database (e.g., SQL Server).
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
Step B: Create Entity Classes (POCOs)
These are plain C# classes. By default, EF Core maps Id to the Primary Key.
public class Product
{
public int Id { get; set; } // Primary Key
public string Name { get; set; } // Column: nvarchar(max)
public decimal Price { get; set; } // Column: decimal(18,2)
public bool IsInStock { get; set; } // Column: bit
}
Step C: Create the DbContext
This class acts as the bridge between your code and the database.
using Microsoft.EntityFrameworkCore;
public class AppDbContext : DbContext
{
// 1. Constructor to accept options (connection string, etc.)
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
// 2. DbSets: represent the tables in your DB
public DbSet<Product> Products { get; set; }
}
Step D: Configuration (Dependency Injection)
In your Program.cs (for ASP.NET Core), register the context with a connection string.
var connectionString = "Server=myServer;Database=MyCodeFirstDb;Trusted_Connection=True;";
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString));
4. Migration Commands
Migrations are the heart of Code First. They track changes to your model over time.
| Command (CLI) | Description |
dotnet ef migrations add InitialCreate | Scans your code, compares it to the last snapshot, and creates a migration file (C#) to bridge the gap. |
dotnet ef database update | Translates the migration file into SQL and executes it on the database. |
dotnet ef migrations remove | Deletes the last migration (only if it hasn't been applied to the DB yet). |
dotnet ef migrations script | Generates a raw SQL script (useful for production deployment). |
Behind the Scenes:
EF Core creates a table called __EFMigrationsHistory in your database. It uses this to check which migrations have already been applied so it doesn't run them twice.
5. Configuring the Schema
EF Core is smart, but sometimes you need to override its assumptions (e.g., making a string required or limiting its length). You have three ways to do this, in order of precedence:
A. Conventions (Default)
EF Core guesses based on naming.
Property named
Id-> Primary Key.string-> nullablenvarchar(max).int-> non-nullableint.
B. Data Annotations (Attributes)
Add attributes directly to your class properties.
public class Product
{
[Key] // Explicit Primary Key
public int ProductKey { get; set; }
[Required] // Not Null
[MaxLength(100)] // nvarchar(100)
public string Name { get; set; }
[NotMapped] // Do not create a column for this
public string TempData { get; set; }
}
C. Fluent API (Most Powerful)
Defined in OnModelCreating inside your DbContext. This keeps your entity classes clean.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.Property(p => p.Name)
.IsRequired()
.HasMaxLength(100);
modelBuilder.Entity<Product>()
.ToTable("tbl_Products"); // Custom table name
}
6. Data Seeding (Initial Data)
You can use Code First to populate the database with default data (like Admin users or Lookup values) automatically.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().HasData(
new Product { Id = 1, Name = "Laptop", Price = 999.99m, IsInStock = true },
new Product { Id = 2, Name = "Mouse", Price = 25.50m, IsInStock = true }
);
}
- Note: When you run
add-migration, EF converts this into SQLINSERTstatements.
7. Handling Changes (Evolution)
Code First is designed for evolving databases.
Modify Code: You add a new property
public string SKU { get; set; }toProduct.Add Migration: Run
dotnet ef migrations add AddedSkuToProduct.Update DB: Run
dotnet ef database update.Result: EF Core sends an
ALTER TABLEcommand to the database to add the column without losing existing data.
8. Advantages vs. Disadvantages
| Advantages | Disadvantages |
| Version Control: Your DB schema lives in Git (as C# code). | Complexity: Mapping complex legacy DB schemas to code can be difficult manually. |
| Speed: Rapid development; no need to switch context to SQL Management Studio. | Data Risks: Careless migrations (e.g., renaming a column) can result in data loss if not reviewed. |
| Type Safety: You work with strongly typed objects immediately. | Learning Curve: Understanding Fluent API for advanced relationships takes time. |
Summary Checklist for Code First
Write Code: Define Entities (
class).Link: Add
DbSet<T>toDbContext.Connect: Configure connection string in
Program.cs.Create:
dotnet ef migrations add Init.Push:
dotnet ef database update.

