Code First in Entity Framework

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

  1. Install EF Core via NuGet:
Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools
  1. 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; }
}
  1. 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

BenefitExplanation
No SQL Schema RequiredCreate database entirely from C# classes
Strong TypingCompile-time safety for queries and data access
Easy RefactoringRenaming or changing properties updates the database via migrations
Domain-DrivenYour classes represent business entities directly
MigrationsEvolve 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

ConceptDescriptionExample
Code FirstDatabase generated from C# classesDbContext + DbSet<T>
DbContextRepresents the database sessionCompanyContext
DbSet<T>Represents a tablepublic DbSet<Employee> Employees { get; set; }
MigrationsUpdate schema when models changeAdd-Migration AddHireDate
RelationshipsNavigation between entitiesDepartment -> Employees
SeedingPopulate default dataHasData()

โœ… 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

  1. Create a Product and Category class with a one-to-many relationship.
  2. Seed some sample categories and products.
  3. Write a LINQ query that returns each category with the number of products and the average price.
  4. 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.