DbContext & Configuration
Level: Beginner
DbContext is your entry point to EF Core. Configuring it properly ensures correct database connections, lazy loading behavior, and query performance. Master this before writing queries.
- DbContext purpose: Bridge between C# code and database, contains DbSet properties for entities
- DbSet definition:
public DbSet<Student> Students { get; set; }represents database table - OnConfiguring method: Configure connection string, logging, lazy loading
- UseSqlServer: Database provider for SQL Server (
options.UseSqlServer(connString)) - Logging configuration: Enable SQL query logging for debugging
- Lazy loading: Automatic loading of related data when accessed (can cause N+1 queries)
- ProxyCreationEnabled: Required for lazy loading (creates proxy classes)
- Connection string: Store in appsettings.json, inject via IConfiguration
- Dependency injection: Register DbContext in Program.cs (
services.AddDbContext<SmsDbContext>()) - DbContextOptions: Immutable configuration passed to DbContext constructor
- Multiple databases: One DbContext per database typically
- SaveChangesAsync: Async version of SaveChanges (preferred in modern apps)
- ChangeTracker: Tracks entity modifications (enable/disable as needed)
- School Management entities: Student, Teacher, Exam, ExamResult, Fee, Attendance
- Common mistakes: Hardcoding connection string, creating DbContext without DI, not disposing context
OnConfiguring Setup
using Microsoft.EntityFrameworkCore;
public class SmsDbContext : DbContext
{
// Entity sets
public DbSet<Student> Students { get; set; }
public DbSet<Teacher> Teachers { get; set; }
public DbSet<Exam> Exams { get; set; }
public DbSet<ExamResult> ExamResults { get; set; }
public DbSet<FeeAccount> FeeAccounts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
if (!options.IsConfigured)
{
options
.UseSqlServer("Server=localhost;Database=SmsDb;User Id=sa;Password=YourPassword;")
.LogTo(Console.WriteLine, LogLevel.Information) // Log SQL queries
.EnableSensitiveDataLogging(); // Log parameter values (development only)
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Configure relationships, constraints here
}
}
Dependency Injection Setup (Program.cs)
var builder = WebApplication.CreateBuilder(args);
// Register DbContext with DI
builder.Services.AddDbContext<SmsDbContext>(options =>
options.UseSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection")
)
);
var app = builder.Build();
appsettings.json Configuration
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=SmsDb;User Id=sa;Password=YourPassword;Connection Timeout=30;Min Pool Size=5;Max Pool Size=100;"
}
}
Using DbContext in Controller
[ApiController]
[Route("api/students")]
public class StudentsController : ControllerBase
{
private readonly SmsDbContext _context;
public StudentsController(SmsDbContext context)
{
_context = context;
}
[HttpGet]
public async Task<ActionResult<List<Student>>> GetStudents()
{
var students = await _context.Students
.Where(s => s.Status == StudentStatus.Active)
.ToListAsync();
return Ok(students);
}
[HttpPost]
public async Task<ActionResult<Student>> CreateStudent([FromBody] CreateStudentRequest request)
{
var student = new Student
{
Name = request.Name,
ClassName = request.ClassName,
Status = StudentStatus.Active
};
_context.Students.Add(student);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetStudent), new { id = student.Id }, student);
}
[HttpGet("{id}")]
public async Task<ActionResult<Student>> GetStudent(int id)
{
var student = await _context.Students.FindAsync(id);
if (student == null)
return NotFound();
return Ok(student);
}
}
Enable Lazy Loading (Optional)
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options
.UseSqlServer(connString)
.UseLazyLoadingProxies() // Enable lazy loading
.LogTo(Console.WriteLine);
}
Then access related data automatically:
var student = await _context.Students.FirstAsync();
var exams = student.Exams; // Lazy loads from database automatically
Logging Configuration
.LogTo(
Console.WriteLine,
new[] { DbLoggerCategory.Database.Command.Name },
LogLevel.Information
)
This logs only SQL commands to console in Development.
Best Practices
✓ Always inject DbContext via DI ✓ Store connection strings in configuration ✓ Use async methods (SaveChangesAsync, ToListAsync) ✓ Dispose DbContext (using statement or DI handles this) ✓ Use separate DbContext per request in web apps
Never hardcode connection strings. Always:
- Store in appsettings.json (dev)
- Use Azure Key Vault or Environment Variables (production)
- Never commit secrets to git
Use ChatGPT, Claude, or Copilot to go deeper on DbContext & Configuration. Try these prompts:
"How do you configure DbContext with dependency injection?""Where should you store connection strings?""What's the difference between OnConfiguring and OnModelCreating?""How do you enable lazy loading in EF Core?"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.