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
- Productivity: Developers can focus on writing business logic without needing deep knowledge of SQL or database design.
- Testability: Easier to write unit tests for your domain models.
- Flexibility: Seamlessly manage database schema evolution using Migrations.
- Abstraction: The database is an implementation detail, allowing for easier swapping of data stores if needed.
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:
- A class ending in "Context" is recognized as a
DbContext
. - A public property of type
DbSet<TEntity>
maps to a table named after the plural of the entity type (e.g.,Blogs
maps to a "Blogs" table). - A property named
Id
or<EntityName>Id
(e.g.,BlogId
) is recognized as the primary key.
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:
- CreateDatabaseIfNotExists: Creates the database if it doesn't exist. Throws an exception if the database exists but its schema doesn't match the model.
- DropCreateDatabaseAlways: Drops the database and recreates it every time the application starts. Useful for development and testing.
- DropCreateDatabaseIfModelChanges: Drops and recreates the database only if the model has changed since the database was created.
- Custom Initializers: You can write your own logic for database initialization.
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:
- Open the Package Manager Console in Visual Studio.
- Run:
Enable-Migrations
This will create a Migrations folder with configuration files.
To create a new migration after making model changes:
- Run:
Add-Migration InitialCreate
(or a descriptive name)
To apply the migration to your database:
- 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.