Mastering Database Design from C# Classes
In modern C# development, Entity Framework (EF) is a powerful ORM (Object-Relational Mapper) that allows developers to work with databases using C# objects instead of raw SQL.
Among EF workflows, Code First stands out for its flexibility: you design your database directly from C# classes, and EF generates the database schema automatically. This approach is ideal for domain-driven design and rapid development.
๐น What Is Code First?
Code First is an Entity Framework approach where your model classes define the database structure.
- No need to create tables manually.
- EF translates classes, properties, and relationships into SQL tables and constraints.
- Supports migrations to evolve the database schema as your application grows.
๐น Setting Up Code First
- Install EF Core via NuGet:
Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools
- Define Model Classes
public class Employee
{
public int Id { get; set; } // Primary Key by convention
public string Name { get; set; }
public string Department { get; set; }
public decimal Salary { get; set; }
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
// Navigation property
public ICollection<Employee> Employees { get; set; }
}
- Define a DbContext
using Microsoft.EntityFrameworkCore;
public class CompanyContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=.;Database=CompanyDB;Trusted_Connection=True;");
}
}
โ
DbSet<T> represents a table.
โ
DbContext represents the database session and is responsible for querying and saving data.
๐น Creating the Database
With Code First, the database can be created automatically:
using (var context = new CompanyContext())
{
context.Database.EnsureCreated();
}
โ
Creates a database based on your classes if it does not exist.
โ
Automatically maps primary keys, relationships, and property types.
๐น Data Seeding
You can populate your database with initial data using the OnModelCreating method:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Department>().HasData(
new Department { Id = 1, Name = "IT" },
new Department { Id = 2, Name = "HR" }
);
modelBuilder.Entity<Employee>().HasData(
new Employee { Id = 1, Name = "Alice", Department = "IT", Salary = 60000 },
new Employee { Id = 2, Name = "Bob", Department = "HR", Salary = 45000 }
);
}
โ Useful for test data or default values.
๐น Migrations โ Evolving the Schema
When your model changes, EF Code First supports migrations to update the database without losing data:
Add-Migration AddHireDate
Update-Database
Example: Adding a new property
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public decimal Salary { get; set; }
public DateTime HireDate { get; set; } // New property
}
โ EF generates SQL to alter the table automatically.
๐น Relationships
One-to-Many: Employee belongs to a Department
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int DepartmentId { get; set; } // Foreign Key
public Department Department { get; set; } // Navigation
}
Many-to-Many: Employees can have multiple Projects
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Employee> Employees { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Project> Projects { get; set; }
}
โ EF automatically creates the join table.
๐น Querying Data with Code First
Code First supports LINQ queries:
using (var context = new CompanyContext())
{
var highEarners = context.Employees
.Where(e => e.Salary > 50000)
.OrderByDescending(e => e.Salary)
.ToList();
foreach (var emp in highEarners)
Console.WriteLine($"{emp.Name} - ยฃ{emp.Salary}");
}
Output:
Alice - ยฃ60000
โ Queries are strongly typed and checked at compile time.
๐น Advantages of Code First
| Benefit | Explanation |
|---|---|
| No SQL Schema Required | Create database entirely from C# classes |
| Strong Typing | Compile-time safety for queries and data access |
| Easy Refactoring | Renaming or changing properties updates the database via migrations |
| Domain-Driven | Your classes represent business entities directly |
| Migrations | Evolve schema without manual SQL |
๐น Real-World Example โ Department Salary Report
using (var context = new CompanyContext())
{
var report = context.Employees
.GroupBy(e => e.Department)
.Select(g => new
{
Department = g.Key,
EmployeeCount = g.Count(),
AverageSalary = g.Average(x => x.Salary)
});
foreach (var dept in report)
Console.WriteLine($"{dept.Department}: {dept.EmployeeCount} staff, avg ยฃ{dept.AverageSalary:F0}");
}
Output:
IT: 1 staff, avg ยฃ60000
HR: 1 staff, avg ยฃ45000
โ Code First and LINQ combine to produce dynamic reports from model classes.
๐ Summary
| Concept | Description | Example |
|---|---|---|
| Code First | Database generated from C# classes | DbContext + DbSet<T> |
| DbContext | Represents the database session | CompanyContext |
| DbSet<T> | Represents a table | public DbSet<Employee> Employees { get; set; } |
| Migrations | Update schema when models change | Add-Migration AddHireDate |
| Relationships | Navigation between entities | Department -> Employees |
| Seeding | Populate default data | HasData() |
โ Best Practices
- Start with clean, well-named model classes.
- Use Data Annotations or Fluent API to configure complex mappings.
- Always migrate instead of dropping the database in production.
- Seed minimal initial data to support testing.
- Use LINQ queries for reporting and filtering โ avoid raw SQL unless necessary.
๐งช Challenge Task
- Create a Product and Category class with a one-to-many relationship.
- Seed some sample categories and products.
- Write a LINQ query that returns each category with the number of products and the average price.
- Display results like:
"Electronics: 3 products, avg ยฃ450"
๐จโ๐ป Want More?
Call James to find out more about:
- Code First, Database First, and Model First workflows
- Relationships, constraints, and navigation properties
- LINQ queries, projections, and reports
- Migrations, seeding, and database evolution
Master Code First to design databases from your domain models, combine it with LINQ for analysis, and create clean, maintainable data-driven applications.