ASP.NET Database with EF Core
Entity Framework Core (EF Core) lets you work with a database using C# classes instead of writing SQL by hand.
What is EF Core?
EF Core is an ORM (Object-Relational Mapper). It maps:
| C# | Database |
|---|---|
| A class | A table |
| A property | A column |
| An object | A row |
A DbSet<T> | A table you can query |
It works with SQL Server, SQLite, PostgreSQL, MySQL and more. This lesson uses SQLite because it needs no installation: the whole database is one file.
Install the Packages
Run these in your project folder:
Example
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet tool install --global dotnet-efThe Entity and the DbContext
Create a new file Data.cs in the project folder and add these two classes:
Example
using Microsoft.EntityFrameworkCore;
public class Student
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Course { get; set; } = "";
public bool FeePaid { get; set; }
}
public class SchoolDb : DbContext
{
public SchoolDb(DbContextOptions<SchoolDb> options) : base(options) { }
public DbSet<Student> Students => Set<Student>();
}A property named Id automatically becomes the primary key.
Register It in Program.cs
Example
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<SchoolDb>(options =>
options.UseSqlite("Data Source=school.db"));
var app = builder.Build();This is dependency injection: ASP.NET Core creates a SchoolDb for each request and gives it to any page or endpoint that asks for it.
Create the Database with Migrations
A migration is a saved set of changes to the database structure.
Example
dotnet ef migrations add InitialCreate
dotnet ef database updateBuild started... Build succeeded. Applying migration '20261015093000_InitialCreate'. Done.
Now school.db exists with a Students table. When you change your classes later, add a new migration and run database update again.
Using It in Minimal APIs
Example
app.MapGet("/students", async (SchoolDb db) =>
await db.Students.OrderBy(s => s.Name).ToListAsync());
app.MapPost("/students", async (Student s, SchoolDb db) =>
{
db.Students.Add(s);
await db.SaveChangesAsync();
return Results.Created($"/students/{s.Id}", s);
});
app.MapPut("/students/{id:int}/paid", async (int id, SchoolDb db) =>
{
var student = await db.Students.FindAsync(id);
if (student is null) return Results.NotFound();
student.FeePaid = true;
await db.SaveChangesAsync();
return Results.NoContent();
});
app.Run();After posting two students, /students returns:
[{"id":2,"name":"Ali","course":"SEO","feePaid":false},{"id":1,"name":"Ayesha","course":"Web Development","feePaid":true}]Using It in a Razor Page
Ask for the SchoolDb in the PageModel's constructor:
Example
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
public class StudentsModel : PageModel
{
private readonly SchoolDb _db;
public StudentsModel(SchoolDb db) => _db = db;
public List<Student> Unpaid { get; private set; } = new();
public async Task OnGetAsync()
{
Unpaid = await _db.Students
.Where(s => !s.FeePaid)
.ToListAsync();
}
}Useful LINQ Queries
| Code | Meaning |
|---|---|
db.Students.ToListAsync() | All rows |
.Where(s => s.Course == "SEO") | Filter rows |
.OrderBy(s => s.Name) | Sort |
.FirstOrDefaultAsync(s => s.Id == 5) | One row or null |
.CountAsync() | Number of rows |
db.Remove(student) | Delete (then call SaveChangesAsync) |
EF Core always uses parameters when it sends your values to the database, so LINQ queries are safe from SQL injection.
Exercise
Exercise
Which method actually writes your added or changed objects to the database?
