Aspnet Ef Core
title: ASP.NET Core Entity Framework Core — Complete Guide to EF Core description: 'Learn Entity Framework Core in ASP.NET: DbContext, entities, LINQ queries, relationships, eager loading, migrations, performance optimization, and best practices.' date: 2026-06-28 lastmod: 2026-06-28 weight: 22 tags: [backend, aspnet]
Entity Framework Core is a lightweight, cross-platform ORM for .NET that maps C# objects to database tables, supporting LINQ queries, change tracking, and migrations.
## What You'll Learn
By the end of this tutorial, you'll configure DbContext, define entities with relationships, write LINQ queries, manage migrations, optimize performance, and handle concurrency.
## Real-World Use
An ASP.NET Core e-commerce app uses EF Core to map Categories, Products, and Orders. LINQ queries fetch data with eager loading. Migrations evolve the schema with zero data loss.
## EF Core Learning Path
```mermaid
flowchart LR
A[Config] --> B[EF Core]
B --> C[Migrations]
C --> D[Auth]
D --> E[JWT]
B --> F{You Are Here}
style F fill:#f90,color:#fff
DbContext Setup
using Microsoft.EntityFrameworkCore;
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Product> Products => Set<Product>();
public DbSet<Category> Categories => Set<Category>();
public DbSet<Order> Orders => Set<Order>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>(entity =>
{
entity.Property(p => p.Price).HasPrecision(18, 2);
entity.HasIndex(p => p.CategoryId);
});
}
}
// Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
Entity Configuration
// Data Annotations
public class Product
{
public int Id { get; set; }
[Required, MaxLength(200)]
public string Name { get; set; } = "";
[Column(TypeName = "decimal(18,2)")]
public decimal Price { get; set; }
public int CategoryId { get; set; }
[ForeignKey(nameof(CategoryId))]
public Category Category { get; set; } = null!;
public ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
}
// Fluent API (in OnModelCreating)
modelBuilder.Entity<Product>(e =>
{
e.Property(p => p.Name).IsRequired().HasMaxLength(200);
e.Property(p => p.Price).HasColumnType("decimal(18,2)");
e.HasOne(p => p.Category)
.WithMany(c => c.Products)
.HasForeignKey(p => p.CategoryId)
.OnDelete(DeleteBehavior.Restrict);
});
LINQ Queries
public class ProductService
{
private readonly AppDbContext _db;
public ProductService(AppDbContext db) => _db = db;
// Basic query
public async Task<List<Product>> GetAllAsync()
{
return await _db.Products.ToListAsync();
}
// Filtering with eager loading
public async Task<List<Product>> GetByCategoryAsync(int categoryId)
{
return await _db.Products
.Include(p => p.Category)
.Where(p => p.CategoryId == categoryId)
.OrderByDescending(p => p.Price)
.ToListAsync();
}
// Projection
public async Task<List<ProductDto>> GetDtosAsync()
{
return await _db.Products
.Select(p => new ProductDto(p.Id, p.Name, p.Price))
.ToListAsync();
}
// Aggregation
public async Task<decimal> GetAveragePriceAsync()
{
return await _db.Products.AverageAsync(p => p.Price);
}
}
Relationships
// One-to-Many
public class Category
{
public int Id { get; set; }
public string Name { get; set; } = "";
public ICollection<Product> Products { get; set; } = new List<Product>();
}
// Many-to-Many (.NET 5+)
public class Student
{
public int Id { get; set; }
public string Name { get; set; } = "";
public ICollection<Course> Courses { get; set; } = new List<Course>();
}
public class Course
{
public int Id { get; set; }
public string Title { get; set; } = "";
public ICollection<Student> Students { get; set; } = new List<Student>();
}
// EF Core 5+ creates the join table automatically
Common Mistakes
1. N+1 Queries
Without Include(), accessing navigation properties in a loop issues N separate queries. Always eager load.
2. Not Using Async
Blocking calls like ToList() instead of ToListAsync() waste threads. Use async methods for I/O.
3. Tracking Too Many Entities
By default, EF Core tracks changes. Use AsNoTracking() for read-only queries to reduce memory.
4. Loading All Data
Fetching thousands of rows without pagination. Use Skip() and Take() for pagination.
5. Ignoring Database Indexes
Queries without proper indexes scan entire tables. Add indexes with HasIndex() in OnModelCreating.
Practice Questions
1. What is EF Core?
An open-source ORM that maps C# objects to database tables with LINQ queries, change tracking, and migrations.
2. How do you eager load related data?
Use .Include(p => p.Category) and .ThenInclude(p => p.SubCategory) for nested relationships.
3. What is the difference between FirstOrDefault and SingleOrDefault?
FirstOrDefault returns first match (or null). SingleOrDefault expects exactly one (throws if multiple found).
4. How do you handle concurrency conflicts?
Add a [Timestamp] byte[] property. EF Core checks it on update and throws DbUpdateConcurrencyException on conflict.
5. Challenge: Create a complete query that returns products with their category names, ordered by price.
var products = await _db.Products
.AsNoTracking()
.Include(p => p.Category)
.Where(p => p.Price > 10)
.OrderByDescending(p => p.Price)
.Select(p => new { p.Name, p.Price, Category = p.Category.Name })
.ToListAsync();
FAQ
Mini Project: Product-Category EF Core Setup
Configure EF Core for a product catalog with categories, including eager loading and queries.
builder.Services.AddDbContext<CatalogDb>(o => o.UseSqlServer(connStr));
public class CatalogDb : DbContext
{
public DbSet<Product> Products => Set<Product>();
public DbSet<Category> Categories => Set<Category>();
}
var products = await db.Products.Include(p => p.Category)
.Where(p => p.Category.Name == "Electronics")
.OrderBy(p => p.Price)
.Select(p => new { p.Name, p.Price })
.ToListAsync();
What's Next
ASP.NET Core Migrations ASP.NET Core Authentication
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro