Entity Framework Core — Complete Guide
In this tutorial, you will learn about Entity Framework Core. We cover key concepts, practical examples, and best practices to help you master this topic.
Hook
Entity Framework Core (EF Core) is the modern object-relational mapper for .NET. It lets you work with databases using C# objects, eliminating the need to write most SQL queries manually. EF Core handles everything from schema creation to complex joins, enabling you to focus on business logic.
Learning Path
graph LR A[EF Core] --> B[DbContext] A --> C[Entity Mapping] B --> D[Migrations] B --> E[LINQ Queries] C --> F[Relationships] style A fill:#4a90d9,color:#fff style B fill:#4a90d9,color:#fff style C fill:#4a90d9,color:#fff style D fill:#4a90d9,color:#fff style E fill:#4a90d9,color:#fff style F fill:#4a90d9,color:#fff
Setting Up EF Core
First, install the required packages and define your entities.
// Install: dotnet add package Microsoft.EntityFrameworkCore.SqlServer
// dotnet add package Microsoft.EntityFrameworkCore.Tools
using Microsoft.EntityFrameworkCore;
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; } = "";
public int Rating { get; set; }
public List<Post> Posts { get; set; } = new();
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; } = "";
public string Content { get; set; } = "";
public int BlogId { get; set; }
public Blog Blog { get; set; } = null!;
}
DbContext
The DbContext class manages database connections and tracks entity changes.
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Configure entity mappings
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.HasMaxLength(500)
.IsRequired();
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.HasForeignKey(p => p.BlogId)
.OnDelete(DeleteBehavior.Cascade);
}
}
Migrations
Migrations keep your database schema in sync with your model classes.
# Create a migration
dotnet ef migrations add InitialCreate
# Apply migrations to database
dotnet ef database update
# Generate SQL script
dotnet ef migrations script
// Programmatic migration
using var context = new BloggingContext();
await context.Database.MigrateAsync();
CRUD Operations
Perform create, read, update, and delete operations through LINQ.
using (var db = new BloggingContext())
{
// Create
var blog = new Blog { Url = "https://example.com", Rating = 5 };
db.Blogs.Add(blog);
db.SaveChanges();
Console.WriteLine($"Created blog with ID: {blog.BlogId}");
// Read
var blogs = db.Blogs
.Where(b => b.Rating >= 3)
.OrderByDescending(b => b.Rating)
.ToList();
Console.WriteLine($"Found {blogs.Count} blogs");
// Update
var existing = db.Blogs.Find(blog.BlogId);
if (existing != null)
{
existing.Rating = 4;
db.SaveChanges();
Console.WriteLine("Rating updated");
}
// Delete
db.Blogs.Remove(existing!);
db.SaveChanges();
Console.WriteLine("Blog deleted");
}
Relationships
EF Core supports one-to-one, one-to-many, and many-to-many relationships.
// Many-to-many (EF Core 5+)
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; } = "";
public List<Course> Courses { get; set; } = new();
}
public class Course
{
public int CourseId { get; set; }
public string Title { get; set; } = "";
public List<Student> Students { get; set; } = new();
}
// OnModelCreating
modelBuilder.Entity<Student>()
.HasMany(s => s.Courses)
.WithMany(c => c.Students)
.UsingEntity(j => j.ToTable("Enrollments"));
Query with Eager Loading
Control how related data is loaded.
using (var db = new BloggingContext())
{
// Eager loading
var blogsWithPosts = db.Blogs
.Include(b => b.Posts)
.ThenInclude(p => p.Comments)
.ToList();
// Explicit loading
var blog = db.Blogs.Find(1);
db.Entry(blog).Collection(b => b.Posts).Load();
// Filtered include
var blogs = db.Blogs
.Include(b => b.Posts.Where(p => p.Title.Contains(".NET")))
.ToList();
}
Common Mistakes
The N+1 query problem: Avoid lazy loading in loops. Use
Includewith eager loading or project withSelect.Not using async methods: Always use
SaveChangesAsync,ToListAsync,FirstOrDefaultAsyncin web applications to avoid thread pool starvation.Tracking overhead for read-only queries: Use
AsNoTracking()for queries that only display data without updating it.Ignoring migrations in production: Use
dotnet ef migrations scriptto generate SQL scripts for production deployment review.Loading entire tables: Always use
Where,Select, and pagination (Skip/Take) instead ofToList()on the entireDbSet.
Practice Questions
Create a
ProductandCategoryrelationship with proper foreign keys and write a query that fetches products by category.Implement a Repository Patternory" >}} pattern that abstracts EF Core operations behind an interface for testability.
Write a Migration that adds an
IsDeletedcolumn for soft delete functionality.Challenge: Build a multi-tenant database architecture where each tenant's data is isolated by a
TenantIdcolumn.
FAQ
Mini Project: Blog Engine
Build a simple blog data access layer with EF Core.
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
public class Author
{
public int AuthorId { get; set; }
public string Name { get; set; } = "";
public string Email { get; set; } = "";
public List<Post> Posts { get; set; } = new();
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; } = "";
public string Content { get; set; } = "";
public DateTime PublishedAt { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; } = null!;
}
public class BlogContext : DbContext
{
public DbSet<Author> Authors => Set<Author>();
public DbSet<Post> Posts => Set<Post>();
public BlogContext(DbContextOptions<BlogContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasIndex(p => p.PublishedAt);
modelBuilder.Entity<Author>()
.HasIndex(a => a.Email).IsUnique();
}
}
// Usage
var options = new DbContextOptionsBuilder<BlogContext>()
.UseSqlite("Data Source=blog.db")
.Options;
using (var db = new BlogContext(options))
{
db.Database.EnsureCreated();
var author = new Author { Name = "Jane Doe", Email = "jane@example.com" };
db.Authors.Add(author);
db.SaveChanges();
var post = new Post
{
Title = "Getting Started with EF Core",
Content = "Entity Framework Core is amazing...",
PublishedAt = DateTime.UtcNow,
AuthorId = author.AuthorId
};
db.Posts.Add(post);
db.SaveChanges();
}
using (var db = new BlogContext(options))
{
var posts = db.Posts
.Include(p => p.Author)
.Where(p => p.PublishedAt > DateTime.UtcNow.AddDays(-7))
.OrderByDescending(p => p.PublishedAt)
.ToList();
foreach (var p in posts)
{
Console.WriteLine($"{p.Title} by {p.Author.Name} on {p.PublishedAt:d}");
}
}
Output:
Getting Started with EF Core by Jane Doe on 6/28/2026
Entity Framework Core transforms database access in C# applications. With LINQ queries, automatic migrations, and change tracking, it is the standard data access technology for modern .NET applications.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro