This folder now contains two finished reference samples you can run and debug:
cd demos/day2/TechConf.Sqlite.Api
dotnet runUseful URLs and files:
- API root:
http://localhost:5100 - Scalar UI:
http://localhost:5100/scalar/v1 - OpenAPI JSON:
http://localhost:5100/openapi/v1.json - HTTP requests:
TechConf.Sqlite.Api/requests.http - Migrations:
TechConf.Sqlite.Api/Data/Migrations/
If you're using the dev container, run the following commands inside the dev container terminal. The dev container already installs dotnet-ef and the latest stable Aspire.Cli.
Start PostgreSQL in Docker with the latest image:
docker run --rm -d \
--name techconf-day2-postgres \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=techconf \
-e POSTGRES_DB=techconfdb \
-p 5432:5432 \
postgres:latestConnection details:
- Host:
localhost - Port:
5432 - Database:
techconfdb - Username:
postgres - Password:
techconf
Then run the sample:
cd demos/day2/TechConf.Postgres.Api
dotnet runUseful URLs and files:
- API root:
http://localhost:5200 - Scalar UI:
http://localhost:5200/scalar/v1 - OpenAPI JSON:
http://localhost:5200/openapi/v1.json - HTTP requests:
TechConf.Postgres.Api/requests.http - Migrations:
TechConf.Postgres.Api/Data/Migrations/
Both samples automatically apply migrations and seed demo data on startup. If you want to stop the PostgreSQL container later, run:
docker stop techconf-day2-postgres- Add NuGet packages:
Npgsql.EntityFrameworkCore.PostgreSQL,Microsoft.EntityFrameworkCore.Design - Create
AppDbContextwithDbSet<Event> - Show Fluent API configuration in
IEntityTypeConfiguration<Event> - Register with
AddDbContextand connection string - Create migration:
dotnet ef migrations add InitialCreate - Apply migration:
dotnet ef database update - Query with LINQ:
db.Events.ToListAsync()
Key teaching moments:
- DbContext lifecycle (scoped by default)
- Fluent API vs Data Annotations
- Migration files and what they contain (dotnet tools)
- Add
SessionandSpeakerentities - Configure one-to-many (Event → Sessions)
- Configure many-to-many (Sessions ↔ Speakers)
- Show
Include()andThenInclude()for eager loading - Demonstrate N+1 problem with and without Include
- Show lifetime differences: Transient vs Scoped vs Singleton
- Create
IEventRepositoryandEventRepository - Register with
AddScoped - Inject into Minimal API endpoint parameters
- Show what happens with wrong lifetime (Singleton DbContext!)
- Show .NET 10 built-in validation:
AddValidation()with[Required],[StringLength] - Create a FluentValidation validator with complex rules
- Build a
ValidationFilter<T>endpoint filter - Show validation error response format
- Add
AddProblemDetails(),UseExceptionHandler(),UseStatusCodePages() - Throw an unhandled exception — show automatic 500 ProblemDetails
- Create
NotFoundExceptioncustom exception - Build
IExceptionHandlerwith pattern matching - Show the ProblemDetails JSON response