Creating Models and Relationships Using Code First in Entity Framework

Designing Databases from Your C# Classes

With Code First in Entity Framework (EF), you start with C# classes to define your data model, and EF generates the corresponding database schema. This approach is ideal for domain-driven design, agile development, or when you want full control over your code first.

Code First supports defining entities, properties, relationships, constraints, and navigation properties – all in code, without an initial database.


๐Ÿ”น What Is Code First?

Code First workflow:

  • Define C# classes representing your entities (tables).
  • Define relationships using navigation properties and data annotations or Fluent API.
  • EF generates the database schema based on your model.
  • Migrations help evolve the database as your models change.

โœ… Offers a code-centric approach to database design while keeping EF in charge of schema creation.


๐Ÿ”น Defining Basic Models

Example: Employee and Department entities:

public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }

    // Navigation property: one-to-many
    public virtual ICollection<Employee> Employees { get; set; } = new List<Employee>();
}

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Salary { get; set; }

    // Foreign key
    public int DepartmentId { get; set; }

    // Navigation property
    public virtual Department Department { get; set; }
}

โœ… virtual keyword enables lazy loading.
โœ… Collections represent one-to-many relationships.


๐Ÿ”น Defining Relationships

1. One-to-Many

  • A Department can have many Employees.
  • Employee has a foreign key DepartmentId and a navigation property Department.
  • Department has an ICollection<Employee> property.

2. Many-to-Many

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Course> Courses { get; set; } = new List<Course>();
}

public class Course
{
    public int Id { get; set; }
    public string Title { get; set; }

    public virtual ICollection<Student> Students { get; set; } = new List<Student>();
}

โœ… EF Core automatically generates a join table for many-to-many relationships.


๐Ÿ”น Data Annotations vs Fluent API

Data Annotations (Attributes)

public class Product
{
    public int Id { get; set; }

    [Required]
    [MaxLength(100)]
    public string Name { get; set; }

    [Column(TypeName = "decimal(18,2)")]
    public decimal Price { get; set; }
}
  • [Required] โ†’ NOT NULL
  • [MaxLength] โ†’ column length
  • [Column(TypeName = "decimal(18,2)")] โ†’ data type in database

Fluent API (In DbContext)

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Employee>()
        .HasOne(e => e.Department)
        .WithMany(d => d.Employees)
        .HasForeignKey(e => e.DepartmentId);
}

โœ… Fluent API gives more control over relationships, keys, table names, and constraints.


๐Ÿ”น Creating the Database

  1. Create a DbContext:
public class CompanyContext : DbContext
{
    public DbSet<Employee> Employees { get; set; }
    public DbSet<Department> Departments { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlServer("Server=.;Database=CompanyDB;Trusted_Connection=True;");
}
  1. Use EF Core Migrations:
Add-Migration InitialCreate
Update-Database

โœ… EF generates tables, relationships, and foreign keys automatically.


๐Ÿ”น Querying Relationships

using (var context = new CompanyContext())
{
    var employees = context.Employees
        .Include(e => e.Department)
        .Where(e => e.Salary > 50000)
        .OrderByDescending(e => e.Salary)
        .ToList();

    foreach (var e in employees)
        Console.WriteLine($"{e.Name} ({e.Department.Name}) - ยฃ{e.Salary}");
}

Output:

Clara (IT) - ยฃ65000
Alice (IT) - ยฃ60000

โœ… Include eager loads related entities.


๐Ÿ”น Advanced Relationships

Composite Keys

modelBuilder.Entity<OrderDetail>()
    .HasKey(od => new { od.OrderId, od.ProductId });

One-to-One

modelBuilder.Entity<User>()
    .HasOne(u => u.UserProfile)
    .WithOne(p => p.User)
    .HasForeignKey<UserProfile>(p => p.UserId);

โœ… Code First supports all EF relationship types.


๐Ÿ”น Best Practices

  • Prefer Fluent API for complex relationships.
  • Use Data Annotations for simple, self-explanatory constraints.
  • Initialize collections to avoid null references.
  • Use migrations for incremental database updates.
  • Keep DbContext lean and focused on your models.

๐Ÿงช Challenge Task

  1. Create models for Department, Employee, and Project.
  2. Define relationships:
    • Department โ†’ Employees (one-to-many)
    • Employee โ†’ Projects (many-to-many)
  3. Configure a DbContext and generate the database.
  4. Write a LINQ query to return:
    • Employee Name
    • Department Name
    • Number of Projects assigned

Expected Output:

Alice (IT) - 3 projects
Bob (HR) - 1 project
Clara (IT) - 2 projects

๐Ÿ‘จโ€๐Ÿ’ป Want More?

Our C# Courses cover:

  • Code First modeling and migrations
  • Relationships: one-to-one, one-to-many, many-to-many
  • Data annotations vs Fluent API
  • LINQ queries, projections, grouping, and joins
  • Best practices for maintainable, domain-driven design

Master Code First to design your database in code, control every aspect of your schema, and write type-safe, maintainable C# applications.