Code-First Approach

The Code-First approach allows you to define your domain-specific objects (your business logic) and then generate a database schema from those objects. This approach is useful when you want to work with your entities in code and let the Object-Relational Mapper (ORM) handle the database creation and management.

What is Code-First?

In Entity Framework Code-First, you start by writing your .NET classes that represent your data model. These classes typically include properties for the data you want to store. Entity Framework then infers the database schema based on these classes and their relationships. You don't need to manually create the database or write SQL scripts initially.

Key Benefits of Code-First

Core Components

1. Domain Models (Entities)

Define your classes to represent the data you want to persist. These are POCO (Plain Old CLR Object) classes.


public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public virtual Blog Blog { get; set; }
}
            

2. DbContext

The DbContext class is your gateway to the database. It represents a session with the database and allows you to query and save data. You expose your entity sets through DbSet<TEntity> properties.


using System.Data.Entity;

public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    public BloggingContext() : base("name=BloggingContext")
    {
        // Optional: Configure initialization strategies
        // Database.SetInitializer(new CreateDatabaseIfNotExists<BloggingContext>());
    }
}
            

3. Conventions

Entity Framework Code-First uses a set of default conventions to map your classes and properties to database tables and columns. For example:

You can override these conventions using Data Annotations or the Fluent API.

Configuring the Model

Using Data Annotations

Decorate your model classes with attributes to configure aspects like primary keys, relationships, and validation.


using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Collections.Generic;

public class Blog
{
    [Key] // Explicitly mark as primary key
    public int BlogId { get; set; }

    [Required] // Make Url non-nullable
    [MaxLength(200)] // Set max length
    public string Url { get; set; }

    public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
}

public class Post
{
    [Key]
    public int PostId { get; set; }

    [Required]
    public string Title { get; set; }

    public string Content { get; set; }

    [ForeignKey("Blog")] // Explicitly define foreign key relationship
    public int BlogId { get; set; }
    public virtual Blog Blog { get; set; }
}
            

Using the Fluent API

Configure your model more extensively within the OnModelCreating method of your DbContext.


using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    public BloggingContext() : base("name=BloggingContext") { }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Disable pluralizing table names
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

        // Configure Blog entity
        modelBuilder.Entity<Blog>()
            .HasKey(b => b.BlogId) // Primary Key
            .Property(b => b.Url)
                .IsRequired()
                .HasMaxLength(200);

        // Configure Post entity and relationship
        modelBuilder.Entity<Post>()
            .HasKey(p => p.PostId) // Primary Key
            .Property(p => p.Title)
                .IsRequired();

        modelBuilder.Entity<Blog>()
            .HasMany(b => b.Posts) // One-to-Many relationship
            .WithRequired(p => p.Blog) // Post must have a Blog
            .HasForeignKey(p => p.BlogId); // Foreign key property
    }
}
            

Tip:

The Fluent API is more powerful and flexible than Data Annotations for complex configurations.

Database Initialization

When you first run your application and access the DbContext, Entity Framework needs to create or update the database. You can control this behavior using Database Initializers:

Set the initializer in the constructor of your DbContext:


public BloggingContext() : base("name=BloggingContext")
{
    // Example: Create database if it doesn't exist
    Database.SetInitializer(new CreateDatabaseIfNotExists<BloggingContext>());
}
            

Note:

For production environments, it's highly recommended to use Entity Framework Migrations instead of automatic database initializers to manage schema changes.

Migrations

Migrations are a powerful feature in Entity Framework Code-First that allow you to evolve your database schema over time as your model changes. They generate incremental scripts to update the database schema.

To enable Migrations:

  1. Open the Package Manager Console in Visual Studio.
  2. Run: Enable-Migrations

This will create a Migrations folder with configuration files.

To create a new migration after making model changes:

  1. Run: Add-Migration InitialCreate (or a descriptive name)

To apply the migration to your database:

  1. Run: Update-Database

You can find more detailed information in the Entity Framework Migrations documentation.

Conclusion

The Code-First approach in Entity Framework provides a flexible and productive way to develop data-driven applications by allowing you to define your model in code and let Entity Framework manage the database schema. Combined with Migrations, it offers a robust solution for handling schema evolution.