EF Core
C# Entity Framework Core ORM DbContext Migration LINQ Repository Pattern Clean Architecture Performance SQL Server PostgreSQL Career Development
| Feature | EF Core | Dapper | ADO.NET | NHibernate |
|---|---|---|---|---|
| Type | Full ORM | Micro ORM | Raw ADO | Full ORM |
| Performance | ดี | ดีมาก | ดีที่สุด | ปานกลาง |
| Productivity | สูงมาก | สูง | ต่ำ | สูง |
| Migration | Built-in | ไม่มี | ไม่มี | FluentMigrator |
| LINQ | Full support | ไม่มี | ไม่มี | Partial |
| Learning | ปานกลาง | ง่าย | ง่าย | สูง |
| เหมาะกับ | Most projects | High perf query | Legacy/control | Complex domain |
DbContext and Models
# === EF Core DbContext Configuration ===
# // Models/Product.cs
# public class Product
# {
# public int Id { get; set; }
# public string Name { get; set; } = string.Empty;
# public decimal Price { get; set; }
# public int CategoryId { get; set; }
# public Category Category { get; set; } = null!;
# public ICollection<OrderItem> OrderItems { get; set; } = new List<OrderItem>();
# public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
# public bool IsActive { get; set; } = true;
# }
#
# // Models/Category.cs
# public class Category
# {
# public int Id { get; set; }
# public string Name { get; set; } = string.Empty;
# public ICollection<Product> Products { get; set; } = new List<Product>();
# }
#
# // Data/AppDbContext.cs
# 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.HasIndex(e => e.Name);
# entity.Property(e => e.Price).HasPrecision(18, 2);
# entity.HasOne(e => e.Category)
# .WithMany(c => c.Products)
# .HasForeignKey(e => e.CategoryId);
# });
# }
# }
from dataclasses import dataclass
@dataclass
class EFCommand:
command: str
description: str
example: str
commands = [
EFCommand("dotnet ef migrations add", "สร้าง Migration ใหม่", "dotnet ef migrations add AddProductTable"),
EFCommand("dotnet ef database update", "Apply Migration ล่าสุด", "dotnet ef database update"),
EFCommand("dotnet ef migrations remove", "ลบ Migration ล่าสุด", "dotnet ef migrations remove"),
EFCommand("dotnet ef migrations list", "แสดง Migration ทั้งหมด", "dotnet ef migrations list"),
EFCommand("dotnet ef migrations script", "สร้าง SQL Script", "dotnet ef migrations script -o migration.sql"),
EFCommand("dotnet ef database drop", "ลบ Database", "dotnet ef database drop --force"),
EFCommand("dotnet ef dbcontext scaffold", "Scaffold จาก DB", 'dotnet ef dbcontext scaffold "ConnStr" Npgsql.EntityFrameworkCore.PostgreSQL'),
]
print("=== EF Core CLI Commands ===")
for c in commands:
print(f" [{c.command}]")
print(f" {c.description}")
print(f" Example: {c.example}")
LINQ Queries and Performance
# === LINQ Query Patterns ===
# // Basic Query
# var products = await _context.Products
# .Where(p => p.IsActive && p.Price > 100)
# .OrderByDescending(p => p.CreatedAt)
# .Take(10)
# .ToListAsync();
#
# // Eager Loading (fix N+1)
# var productsWithCategory = await _context.Products
# .Include(p => p.Category)
# .Where(p => p.IsActive)
# .ToListAsync();
#
# // Projection (select only needed fields)
# var productDtos = await _context.Products
# .Where(p => p.IsActive)
# .Select(p => new ProductDto
# {
# Id = p.Id,
# Name = p.Name,
# Price = p.Price,
# CategoryName = p.Category.Name
# })
# .ToListAsync();
#
# // AsNoTracking (read-only performance)
# var readOnlyProducts = await _context.Products
# .AsNoTracking()
# .Where(p => p.CategoryId == categoryId)
# .ToListAsync();
#
# // Compiled Query (frequently called)
# private static readonly Func<AppDbContext, int, Task<Product?>> GetProductById =
# EF.CompileAsyncQuery((AppDbContext ctx, int id) =>
# ctx.Products.FirstOrDefault(p => p.Id == id));
@dataclass
class PerfTip:
problem: str
solution: str
impact: str
code_change: str
tips = [
PerfTip("N+1 Query", "Use Include() for Eager Loading", "10x faster for related data",
".Include(p => p.Category)"),
PerfTip("Tracking overhead", "Use AsNoTracking() for reads", "2-3x faster read queries",
".AsNoTracking()"),
PerfTip("Over-fetching", "Use Select() Projection", "Reduce data transfer 50-80%",
".Select(p => new { p.Id, p.Name })"),
PerfTip("Repeated queries", "Use Compiled Query", "Eliminate query compilation cost",
"EF.CompileAsyncQuery(...)"),
PerfTip("Slow inserts", "Use AddRange() + batch", "10-100x faster bulk insert",
"_context.Products.AddRange(list)"),
PerfTip("Missing index", "Add HasIndex in OnModelCreating", "100x faster filtered queries",
"entity.HasIndex(e => e.Name)"),
PerfTip("Complex query", "Use Raw SQL via FromSqlRaw", "Optimal for complex joins/CTEs",
'_context.Products.FromSqlRaw("SELECT ...")'),
]
print("\n=== Performance Tips ===")
for t in tips:
print(f" [{t.problem}] → {t.solution}")
print(f" Impact: {t.impact}")
print(f" Code: {t.code_change}")
Repository Pattern
# === Repository Pattern with EF Core ===
# // IRepository.cs
# public interface IRepository<T> where T : class
# {
# Task<T?> GetByIdAsync(int id);
# Task<IEnumerable<T>> GetAllAsync();
# Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate);
# Task AddAsync(T entity);
# void Update(T entity);
# void Remove(T entity);
# }
#
# // Repository.cs
# public class Repository<T> : IRepository<T> where T : class
# {
# protected readonly AppDbContext _context;
# protected readonly DbSet<T> _dbSet;
#
# public Repository(AppDbContext context)
# {
# _context = context;
# _dbSet = context.Set<T>();
# }
#
# public async Task<T?> GetByIdAsync(int id) => await _dbSet.FindAsync(id);
# public async Task<IEnumerable<T>> GetAllAsync() => await _dbSet.ToListAsync();
# public async Task AddAsync(T entity) => await _dbSet.AddAsync(entity);
# public void Update(T entity) => _dbSet.Update(entity);
# public void Remove(T entity) => _dbSet.Remove(entity);
# }
#
# // IUnitOfWork.cs
# public interface IUnitOfWork : IDisposable
# {
# IRepository<Product> Products { get; }
# IRepository<Category> Categories { get; }
# Task<int> SaveChangesAsync();
# }
@dataclass
class CareerLevel:
level: str
ef_skills: str
salary_range: str
project_type: str
levels = [
CareerLevel("Junior .NET Dev", "Basic CRUD, Migration, LINQ basics",
"25-45K THB", "Simple CRUD apps, internal tools"),
CareerLevel("Mid .NET Dev", "Repository Pattern, Performance tuning, Complex LINQ",
"45-80K THB", "Web API, microservices, e-commerce"),
CareerLevel("Senior .NET Dev", "Clean Architecture, CQRS, Advanced EF, Raw SQL",
"80-150K THB", "Enterprise apps, system design"),
CareerLevel(".NET Architect", "Multi-DB strategy, Sharding, Event Sourcing",
"120-200K+ THB", "Platform design, team leadership"),
]
print("Career Path (.NET + EF Core):")
for l in levels:
print(f" [{l.level}] Salary: {l.salary_range}")
print(f" EF Skills: {l.ef_skills}")
print(f" Projects: {l.project_type}")
เคล็ดลับ
- AsNoTracking: ใช้ AsNoTracking() สำหรับ Read-only Query เสมอ
- Include: ใช้ Include() แก้ N+1 Problem ทุกครั้ง
- Migration: สร้าง SQL Script จาก Migration ตรวจสอบก่อน Apply Production
- Index: ตั้ง Index บน Column ที่ใช้ Where/OrderBy บ่อย
- Logging: เปิด EF Core Logging ดู Generated SQL ตรวจ Performance
Entity Framework Core คืออะไร
ORM .NET C# Class Database SQL Server PostgreSQL MySQL Code-First Database-First Migration LINQ Query Schema Change
ช่วยพัฒนาอาชีพ IT อย่างไร
.NET Developer ทุกตำแหน่ง ASP.NET Core Blazor MAUI Resume Repository Unit of Work CQRS Performance Portfolio Clean Architecture
Migration ทำอย่างไร
dotnet ef migrations add update remove list script SQL CI/CD Pipeline Production Rollback Schema Change
ปรับ Performance อย่างไร
N+1 Include Eager Loading AsNoTracking Read-only Select Projection Compiled Query Batch Insert Index Raw SQL Logging Generated SQL
สรุป
C# Entity Framework Core ORM DbContext Migration LINQ Repository Pattern Performance AsNoTracking Include Clean Architecture Career Development Production
