EngineeringSenior DeveloperInterview Sessions

Interview Questions - Phase 1: .NET Core & C# Mastery

Dành cho level Senior đến Solution Architect. Câu hỏi theo kiểu scenario-based design - trả lời tư duy, không cần code dài.


1. Async/Await & Concurrency

Câu hỏi lý thuyết

  1. [Senior] Khi nào gọi Task.Run trong ASP.NET Core request pipeline là một bad practice? Hãy giải thích Thread Pool starvation diễn ra như thế nào.
  2. [Senior] Phân biệt Task và ValueTask. Trong trường hợp đọc dữ liệu từ cache memory, bạn sẽ thiết kế method signature dùng Task<T> hay ValueTask<T>, và tại sao?
  3. [Architect] Bạn có một API endpoint nhận file upload lên đến 500MB và ghi vào disk. Nếu sử dụng IFormFile.CopyToAsync, memory API có thể spike. Giải pháp của bạn là gì để tối ưu memory footprint và vẫn không block thread?
  4. [Architect] Giải thích vấn đề "Captive Dependency" trong DI khi kết hợp giữa Singleton và Scoped service. Hậu quả là gì đối với một DbContext bị capture?

🎯 Use Case 1: Dashboard Aggregation API

Có một API GET /dashboard/{userId} cần lấy:

  • Profile từ User Service (HTTP)
  • 10 orders gần nhất từ Database (EF Core)
  • Số thông báo chưa đọc từ Notification Service (HTTP)
  • Điểm loyalty từ Loyalty Service (HTTP)

Trả lời theo góc nhìn design: 5. [Senior] Những phần nào có thể chạy song song? Có rủi ro gì nếu dùng Task.WhenAll bừa? 6. [Senior] Nếu DB dùng EF Core, bạn sẽ lưu ý gì khi chạy song song? 7. [Architect] Khi nào nên fail toàn bộ request, khi nào nên trả partial data (graceful degradation)? 8. [Architect] CancellationToken nên đi qua những đâu? Nếu Notification Service mất 8 giây, bạn xử lý thế nào?

🎯 Use Case 2: High-Throughput Background Processor

Bạn có một BackgroundService xử lý 5,000 messages/giây từ Azure Service Bus. Mỗi message cần:

  • Deserialize JSON
  • Validate business rules
  • Ghi DB
  • Publish event tiếp
  1. [Senior] Bạn sẽ dùng Channel<T> hay BlockingCollection<T>? Giải thích tại sao Channel<T> là async-first.
  2. [Architect] Nếu processing rate < incoming rate, bạn thiết kế backpressure thế nào? Khi nào nên reject message vs khi nào nên buffer?

⚠️ Anti-Patterns - Async/Concurrency

Những sai lầm thường gặp ở các dự án .NET thực tế

#Anti-PatternTại sao nguy hiểm
1Async naming, sync implementation - GetDataAsync() nhưng bên trong gọi .ToList() hoặc .ResultFake async - vẫn block thread, misleading API contract
2Bọc sync I/O bằng Task.Run - await Task.Run(() => File.ReadAllText(...))Tưởng là async, thật ra chỉ chuyển block sang thread pool thread khác
3Controller async, Service sync - Controller dùng await, nhưng Service bên trong gọi sync DBBề ngoài hiện đại, ruột vẫn blocking. Thread vẫn bị block
4Fire-and-forget trong request - _ = SendEmailAsync() không awaitMất lỗi, mất dữ liệu, mất kiểm soát. Request trả OK nhưng email chưa chắc gửi
5Task.WhenAll với cùng DbContext - Song song query trên 1 DbContext instanceDbContext KHÔNG thread-safe. Bug ngẫu nhiên, data corruption
6Không phân biệt CPU-bound vs I/O-bound - Dùng Task.Run cho HTTP call, dùng async cho tính toán nặngChọn sai primitive → ThreadPool starvation hoặc performance loss
7ConfigureAwait(false) bừa bãi trong ASP.NET CoreASP.NET Core không có SynchronizationContext, ConfigureAwait(false) vô tác dụng nhưng gây confuse
8Deadlock bằng .Result / .Wait() - service.GetAsync().Result trong sync methodClassic deadlock trong ASP.NET (có SyncContext), thread starvation trong ASP.NET Core

2. Memory Management & Performance

Câu hỏi lý thuyết

  1. [Senior] Trình bày vòng đời của Garbage Collector (Gen 0, Gen 1, Gen 2, LOH). Việc gọi GC.Collect() thủ công thường có hại hơn là có lợi, tại sao?
  2. [Senior] string trong C# là immutable. Giải thích cơ chế string.Intern() và khi nào tính năng này gây memory leak?

🎯 Use Case 3: Real-time Message Processor Memory Spike

Một background service xử lý 10,000 MQTT messages/giây. Mỗi xử lý cấp phát chuỗi (string concatenation) và byte array. App bị CPU spike và latency cao do GC chạy liên tục.

  1. [Senior] Bạn sẽ tối ưu thế nào với ArrayPool<byte>, Span<T>, và string.Create()?
  2. [Architect] Monitor bằng cách nào? Bạn sẽ dùng metrics gì để biết GC đang là bottleneck (GC Pause %, Gen2 Collections/sec)?
  3. [Architect] Quyết định chọn Server GC vs Workstation GC dựa vào tiêu chí gì?

🎯 Use Case 4: API Response Time Regression

Sau khi deploy version mới, API /api/products chậm hơn 3x (từ 50ms → 150ms). Không thay đổi DB query. BenchmarkDotNet local chạy bình thường.

  1. [Senior] Bạn sẽ diagnose bằng những tool nào? (dotnet-counters, dotnet-trace, Application Insights)
  2. [Architect] Kể 3 nguyên nhân thường gặp nhất mà "code không đổi nhưng chậm hơn" trong .NET.

⚠️ Anti-Patterns - Memory & Performance

#Anti-PatternTại sao nguy hiểm
1String concatenation trong loop - result += item.Name + ","Mỗi lần tạo string mới → GC pressure, O(n²) memory
2ToList() khi chỉ cần iterate - .ToList().Where(...) thay vì .Where(...)Cấp phát List không cần thiết, đặc biệt tệ với large dataset
3Boxing value types - object obj = 42; hoặc non-generic collectionsHeap allocation cho mỗi value type → GC pressure
4Không dispose IDisposable - quên using cho HttpClient, Stream, DbContextMemory leak, connection leak, file handle exhaustion
5LINQ materialization quá sớm - .ToList() rồi mới .Where().Select()Load toàn bộ data vào memory rồi mới filter thay vì push filter xuống DB

3. Entity Framework Core Khắc Nghiệt

Câu hỏi lý thuyết

  1. [Senior] N+1 query là gì và làm sao để phát hiện? So sánh Include() (eager loading) vs Projection (Select()) vs Split Queries.
  2. [Senior] Giải thích cách EF Core ChangeTracker hoạt động. Tại sao AsNoTracking() lại làm query lấy danh sách read-only nhanh hơn đáng kể?

🎯 Use Case 5: E-Commerce Product Listing Performance

API GET /api/products cần trả danh sách 50 products kèm:

  • Category name (FK)
  • Average rating (từ bảng Reviews - hàng triệu records)
  • Thumbnail URL (từ bảng ProductImages)
  • Inventory status (từ bảng Stock)
  1. [Senior] Viết query bằng EF Core mà KHÔNG gây N+1. Bạn chọn Include hay Select projection? Tại sao projection thường tốt hơn cho API responses?
  2. [Senior] Khi nào Split Query (AsSplitQuery()) lại TỆ hơn single query? (Hint: think about data consistency trong 2 roundtrips)
  3. [Architect] Nếu bảng Products có 10 triệu records, bạn có dùng EF Core cho search/filter không? Khi nào nên bypass EF dùng Dapper hoặc Raw SQL?

🎯 Use Case 6: Bulk Data Import

Hệ thống cần import 500,000 records từ CSV vào SQL Server mỗi đêm. Team hiện dùng foreach + AddAsync + SaveChangesAsync mỗi record.

  1. [Senior] Tại sao cách hiện tại có thể mất hàng GIỜ? ChangeTracker đang làm gì ở 500K records?
  2. [Architect] Thiết kế pipeline import: dùng EF Core ExecuteUpdate/ExecuteDelete, SqlBulkCopy, hay staging table + MERGE? Trade-offs?

⚠️ Anti-Patterns - EF Core

#Anti-PatternTại sao nguy hiểm
1Inject DbContext vào Singleton serviceDbContext là Scoped. Captive dependency → stale data, memory leak
2SaveChanges trong loop - foreach(item) { db.Add(item); await db.SaveChangesAsync(); }1 transaction per record = 500K roundtrips. Dùng AddRange + 1 SaveChanges
3SELECT * khi chỉ cần 3 fields - .ToListAsync() trả entity đầy đủLoad toàn bộ columns vào memory, ChangeTracker track không cần thiết
4Lazy loading trong API - Navigation property tự load khi serialize JSONN+1 ẩn: serializer truy cập property → trigger DB query cho MỖI entity
5DbContext dùng chung giữa threads - Task.WhenAll(query1, query2) cùng DbContextDbContext KHÔNG thread-safe. Kết quả random, exceptions khó debug
6Quên AsNoTracking cho read-only queriesChangeTracker snapshot mỗi entity → chậm và tốn memory với large datasets
7OrderBy trên column không có index - .OrderByDescending(x => x.CreatedAt) mà CreatedAt chưa indexFull table scan trên triệu records. DBA sẽ giết bạn

4. Middleware & DI

Câu hỏi lý thuyết

  1. [Senior] Trong ASP.NET Core, thứ tự đăng ký Middleware cực kỳ quan trọng. Hãy cho biết hậu quả nếu UseAuthorization() được đặt trước UseAuthentication().
  2. [Senior] DI Lifetime: Singleton, Scoped, Transient. Vẽ ra sơ đồ khi nào mỗi instance được tạo và dispose trong 1 HTTP request lifecycle.

🎯 Use Case 7: Multi-Tenant API

Bạn xây API cho SaaS product phục vụ 200 tenants, mỗi tenant có DB riêng (database-per-tenant).

  1. [Senior] Middleware nào sẽ extract TenantId từ request? (Header, subdomain, JWT claim?) Architecture decision của bạn?
  2. [Architect] Làm sao inject đúng connection string cho DbContext theo từng tenant? Bạn sẽ dùng IDbContextFactory, factory pattern, hay custom scope?
  3. [Architect] Rate limit per-tenant: tenant Free 100 req/min, tenant Pro 10,000 req/min. Thiết kế middleware + data store cho distributed environment?

🎯 Use Case 8: Custom Distributed Rate Limiter

Bạn cần thiết kế rate limit middleware cho API chạy 5 pods đằng sau Load Balancer.

  1. [Architect] In-memory counter sẽ fail thế nào? Tại sao mỗi pod count riêng = rate limit sai?
  2. [Architect] Bạn chọn Redis Sliding Window hay Fixed Window? Trade-offs? Có cần Lua script không?

⚠️ Anti-Patterns - DI & Middleware

#Anti-PatternTại sao nguy hiểm
1Service Locator - var service = serviceProvider.GetService<T>() thay vì constructor injectẨn dependencies, khó test, violate DIP
2God Service - 1 class inject 15+ dependenciesVi phạm SRP. Class làm quá nhiều thứ. Cần tách nhỏ
3Scoped trong Singleton - Singleton service inject Scoped serviceCaptive dependency: Scoped sống lâu bằng Singleton → stale data
4Middleware order sai - CORS sau AuthorizationRequest bị reject trước khi CORS headers được set → browser block
5Catch-all exception middleware nuốt lỗi - catch (Exception) { return 500; } không logMất thông tin debug. Production incident mù mờ

👉 Xem đáp án chi tiết tại: Answers/ans-01-dotnet-csharp.md