Entity Framework Code-First Approach
Leveraging your domain-specific classes to build a data model.
Note: This documentation covers the Entity Framework Code-First approach. For other Entity Framework development approaches, please navigate to the relevant sections.
Introduction
The Entity Framework (EF) Code-First approach allows you to create a conceptual model and a data model based on your domain-specific classes. EF generates the database schema for you. This paradigm is particularly useful when you want to start developing your application by defining your business logic and domain objects first, rather than designing the database schema upfront.
Code-First provides a flexible and developer-centric way to build data access layers. It empowers developers to work with rich domain objects directly, abstracting away the complexities of database schema management during the initial development phases.
Key Benefits of Code-First
- Developer Productivity: Focus on business logic and domain modeling.
- Database Agnosticism: Easily switch between different database providers.
- Rapid Prototyping: Quickly iterate on your domain models and see them reflected in the database.
- Testability: Easier to mock and test data access logic.
Getting Started with Code-First
To use Code-First, you'll define your domain classes, and then use EF to map them to a database. EF will then generate the database schema based on these classes.
1. Define Your Domain Classes
Start by creating plain .NET classes that represent your entities. These classes typically don't need to inherit from any EF-specific base classes.
namespace MyProject.Models
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public virtual Category Category { get; set; }
public int CategoryId { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
}
2. Create a Derived Context
Create a class that derives from System.Data.Entity.DbContext
. This context class represents a session with the database and is used to query and save data.
using System.Data.Entity;
using MyProject.Models;
namespace MyProject.Data
{
public class MyDatabaseContext : DbContext
{
public MyDatabaseContext() : base("name=MyConnectionString") // Or connection string directly
{
}
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Optional: Further configuration using Fluent API can go here
base.OnModelCreating(modelBuilder);
}
}
}
The DbSet<TEntity>
properties represent collections of entities in your context, which will typically correspond to tables in your database.
3. Configure the Database Connection
The connection string can be specified in your application's configuration file (e.g., App.config
or Web.config
) or passed directly to the context constructor.
<configuration>
<connectionStrings>
<add name="MyConnectionString"
connectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=MyDatabase;Integrated Security=True;"
providerName="System.Data.SqlClient" />
</connectionStrings>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework" />
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
</configuration>
Database Initialization Strategies
When EF Code-First starts for the first time or when the model changes, it needs to create or update the database. EF provides several initialization strategies:
1. CreateDatabaseIfNotExists
This is the default strategy. If the database doesn't exist, it's created. If it exists but the model has changed since it was created, an exception is thrown. This is ideal for development.
// Default behavior, no explicit code needed if this is desired.
// You can also explicitly set it:
Database.SetInitializer(new CreateDatabaseIfNotExists<MyDatabaseContext>());
2. DropCreateDatabaseAlways
This strategy drops the database and recreates it every time the application starts. All existing data is lost.
Database.SetInitializer(new DropCreateDatabaseAlways<MyDatabaseContext>());
3. DropCreateDatabaseIfModelChanges
This strategy drops and recreates the database if the model has changed since the database was created. Data is lost when the model changes.
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyDatabaseContext>());
4. MigrateDatabaseToLatestVersion
This strategy is part of EF Migrations. It ensures the database schema is updated to the latest version whenever the model changes. Data is preserved.
// Typically configured via Migrations, but can be set programmatically:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDatabaseContext, MyProject.Migrations.Configuration>());
EF Migrations is the recommended approach for managing database schema evolution in production environments.
Configuring the Model
While conventions handle many mapping scenarios automatically, you can use the Fluent API or Data Annotations to customize your model's configuration.
Using Data Annotations
Decorate your domain classes with attributes from the System.ComponentModel.DataAnnotations
and System.ComponentModel.DataAnnotations.Schema
namespaces.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MyProject.Models
{
[Table("ProductsTable")] // Renames the table
public class Product
{
[Key] // Marks ProductId as the primary key
public int ProductId { get; set; }
[Required] // Makes the Name column non-nullable
[MaxLength(100)] // Sets the maximum length of the Name column
public string Name { get; set; }
[Column(TypeName = "decimal(18,2)")] // Specifies the exact SQL Server data type
public decimal Price { get; set; }
[ForeignKey("Category")] // Configures the relationship and foreign key
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
public class Category
{
[Key]
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
}
Using the Fluent API
Configure your model within the OnModelCreating
method of your DbContext
.
using System.Data.Entity;
using MyProject.Models;
namespace MyProject.Data
{
public class MyDatabaseContext : DbContext
{
public MyDatabaseContext() : base("name=MyConnectionString") { }
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure Product entity
modelBuilder.Entity<Product>()
.ToTable("ProductsTable") // Renames the table
.HasKey(p => p.ProductId); // Sets the primary key
modelBuilder.Entity<Product>()
.Property(p => p.Name)
.IsRequired() // Makes the Name column non-nullable
.HasMaxLength(100); // Sets the maximum length
modelBuilder.Entity<Product>()
.Property(p => p.Price)
.HasPrecision(18, 2); // Sets the decimal precision
// Configure Category entity
modelBuilder.Entity<Category>()
.HasMany(c => c.Products) // One-to-many relationship
.WithRequired(p => p.Category) // Product must have a Category
.HasForeignKey(p => p.CategoryId); // Specifies the foreign key column
base.OnModelCreating(modelBuilder);
}
}
}
Entity Framework Migrations
EF Migrations is a powerful tool that allows you to evolve your database schema over time without losing data. It allows you to track changes to your model and generate corresponding SQL scripts to update your database.
Enabling Migrations
Open the Package Manager Console in Visual Studio and run the following command:
Enable-Migrations
This will create a Migrations
folder in your project containing a Configuration.cs
file. You can configure your migration settings here, including setting the default initializer to MigrateDatabaseToLatestVersion
.
Creating a Migration
When you make changes to your domain models or configurations, create a new migration:
Add-Migration InitialCreate
This command generates a new migration file. You can review and modify the generated code if necessary.
Applying Migrations
To apply the pending migrations to your database:
Update-Database
This command will execute the SQL scripts defined in your migration files to update the database schema.
Conclusion
The Entity Framework Code-First approach offers a streamlined development experience by prioritizing your domain model. By defining your entities and relationships in code, EF handles the complexities of database schema generation and management. Coupled with EF Migrations, you gain a robust system for evolving your database alongside your application.