When querying in Entity Framework Core, both .Include()
and .Select()
can help shape your data — but they serve different purposes.
1. .Include()
→ Load Related Data (Eager Loading)
-
Tells EF Core to load related navigation properties from the database.
-
Returns full entities — including tracked states (unless you use .AsNoTracking()
).
-
Best when you need all related entity details.
Good when: You need the related entities fully loaded.
Bad when: You only need a few fields (it wastes memory).
Example:
var orders = await _context.Orders
.Include(o => o.Customer)
.ToListAsync();
var orders = await _context.Orders .Include(o => o.Customer) .ToListAsync();
2. .Select()
→ Shape Data / Projection
-
Projects into custom objects (DTOs, anonymous types, etc.).
-
Loads only the fields you need, improving performance.
-
Best for lightweight queries.
Good when: You want lightweight queries & better performance.
Bad when: You actually need full entity tracking.
Example:
var orders = await _context.Orders
.Select(o => new { o.Id, CustomerName = o.Customer.Name })
.ToListAsync();
var orders = await _context.Orders .Select(o => new { o.Id, CustomerName = o.Customer.Name }) .ToListAsync();
Common Mistake
.NET 8 & EF Core 9 Example Without Database Connection
Here’s a quick in-memory simulation that demonstrates both approaches:
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
using var context = new AppDbContext();
// Seed in-memory data
var customer = new Customer { Id = 1, Name = "John Doe" };
var order1 = new Order { Id = 101, Customer = customer };
var order2 = new Order { Id = 102, Customer = customer };
context.Customers.Add(customer);
context.Orders.AddRange(order1, order2);
await context.SaveChangesAsync();
// 1️⃣ Using .Include() - Full entity with relationships
var fullOrders = await context.Orders
.Include(o => o.Customer)
.ToListAsync();
Console.WriteLine("Using Include():");
foreach (var order in fullOrders)
{
Console.WriteLine($"Order {order.Id}, Customer: {order.Customer.Name}");
}
// 2️⃣ Using .Select() - Lightweight projection
var lightweightOrders = await context.Orders
.Select(o => new { o.Id, CustomerName = o.Customer.Name })
.ToListAsync();
Console.WriteLine("\nUsing Select():");
foreach (var order in lightweightOrders)
{
Console.WriteLine($"Order {order.Id}, Customer: {order.CustomerName}");
}
}
}
// EF Core Models
public class Order
{
public int Id { get; set; }
public Customer Customer { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
// DbContext with InMemory Provider
public class AppDbContext : DbContext
{
public DbSet<Order> Orders => Set<Order>();
public DbSet<Customer> Customers => Set<Customer>();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseInMemoryDatabase("EFCoreDemo"); // No real DB required
}
}
Output
Using Include():
Order 101, Customer: John Doe
Order 102, Customer: John Doe
Using Select():
Order 101, Customer: John Doe
Order 102, Customer: John Doe
Conclusion
✅ With .Include()
, you get full entities and tracking — useful for updates and complete data operations.
✅ With .Select()
, you only pull what you need, making your queries lighter and faster.