.NET Minimal API Services with EF Core and CQRS
Production-ready .NET minimal API services: clean architecture, EF Core, CQRS handlers, JWT auth, and AOT checks.
Before & after
Same prompt, same agent — the only difference is the skill.
I can build a REST API for product management using Entity Framework. There are a few architectural approaches we could take here. We could go with a simple three-layer architecture (controller → service → data access), or if you want something more sophisticated, I could use CQRS with MediatR for cleaner separation of concerns. We'd need to set up Entity Framework Core with migrations, handle authentication (probably JWT), and structure it for cloud deployment. Do you have a preference on whether you want dependency injection set up or custom patterns? Also, should I include logging and error handling from the start?
REST API: PRODUCT MANAGEMENT (.NET 8)
PROJECT STRUCTURE
ProductsApi.sln
├── ProductsApi.Core
│ ├── Entities/
│ │ ├── Product.cs
│ │ └── Category.cs
│ ├── Interfaces/
│ │ ├── IProductRepository.cs
│ │ └── IUnitOfWork.cs
│ └── ValueObjects/
│ └── Money.cs (price representation)
│
├── ProductsApi.Application
│ ├── Commands/
│ │ ├── CreateProductCommand.cs
│ │ ├── UpdateProductCommand.cs
│ │ └── CreateProductCommandHandler.cs (MediatR)
│ ├── Queries/
│ │ ├── GetProductByIdQuery.cs
│ │ └── GetProductByIdQueryHandler.cs
│ ├── DTOs/
│ │ ├── CreateProductDto.cs
│ │ └── ProductResponseDto.cs
│ └── Validators/
│ └── CreateProductValidator.cs (FluentValidation)
│
├── ProductsApi.Infrastructure
│ ├── Persistence/
│ │ ├── ApplicationDbContext.cs (EF Core)
│ │ ├── Repositories/
│ │ │ └── ProductRepository.cs
│ │ └── Migrations/
│ │ └── InitialCreate.cs
│ ├── Authentication/
│ │ └── JwtTokenService.cs
│ └── Logging/
│ └── SerilogConfiguration.cs
│
└── ProductsApi.Api
├── Program.cs (dependency injection setup)
├── Endpoints/
│ └── ProductEndpoints.cs (minimal APIs)
└── Middleware/
└── ExceptionHandlingMiddleware.cs
===== KEY FILES =====
DATABASE SETUP (Entities)
Product.cs:
• Id (GUID primary key)
• Name (string, required, max 100)
• Description (string, nullable)
• Price (Money value object)
• CategoryId (FK to Category)
• CreatedAt (DateTime, audit)
• UpdatedAt (DateTime, audit)
Entity configuration (Fluent API in DbContext):
• Product table has unique index on Name
• Foreign key cascade delete disabled (soft delete instead)
===== COMMANDS (CQRS) =====
CreateProductCommand
→ Input: name, description, price, categoryId
→ Validation: FluentValidation (price > 0, name length)
→ Handler: Persists to EF Core, returns ProductId
→ Response: 201 Created with Location header
UpdateProductCommand
→ Input: id, name, description, price
→ Concurrency: Optimistic locking (timestamp column)
→ Handler: Updates existing product, validates new price
→ Response: 200 OK or 409 Conflict if stale
===== QUERIES =====
GetProductByIdQuery
→ Input: productId
→ Handler: Queries EF Core with AsNoTracking (read-only optimization)
→ Response: ProductResponseDto or 404
GetProductsQuery
→ Input: pageNumber, pageSize, categoryId (optional)
→ Handler: IQueryable filtering + pagination (skip/take)
→ Response: Paginated list, includes total count header
===== ENDPOINTS (Minimal APIs) =====
app.MapGet("/products/{id}", GetProductById)
app.MapPost("/products", CreateProduct)
app.MapPut("/products/{id}", UpdateProduct)
app.MapDelete("/products/{id}", DeleteProduct)
Each endpoint:
• Validates auth (JWT middleware)
• Maps request DTO to command/query
• Uses MediatR to dispatch
• Maps response DTO for serialization
===== AUTHENTICATION (JWT) =====
JwtTokenService:
• Signs token with RSA private key (4096-bit)
• Includes claims: userId, email, role
• Expiry: 15 minutes (access token)
• Refresh token: 7 days in HttpOnly cookie
Program.cs setup:
• AddAuthentication("Bearer")
• AddJwtBearer (validate signature, expiry)
• Middleware order: Auth → Authorization
===== ERROR HANDLING =====
ExceptionHandlingMiddleware catches:
• ValidationException → 400 Bad Request
• NotFoundException → 404 Not Found
• DbUpdateConcurrencyException → 409 Conflict
• Unhandled exceptions → 500 (logged, no stack trace exposed)
Response format:
{
"error": "Product not found",
"traceId": "0HN1GDGA2P9R0:00000001",
"statusCode": 404
}
===== DEPLOYMENT (AOT COMPILATION) =====
Program.cs AOT settings:
• UseNativeAot() enabled
• Reflection-free DI setup (types registered explicitly)
• Build time: dotnet publish -c Release -r linux-x64
Container image: 150 MB (vs 600+ with JIT)
Cold start: <50ms (vs 500ms with JIT)
===== MIGRATIONS =====
Initial setup:
→ Command: dotnet ef migrations add InitialCreate
→ Command: dotnet ef database update
Schema design:
• Products table (id, name, description, price, categoryId, createdAt, updatedAt)
• Categories table (id, name)
• Foreign key: Products.categoryId → Categories.id (no cascade delete)
===== TESTING =====
Test project uses:
• xUnit (test runner)
• Moq (mocking repositories)
• In-memory EF Core for integration tests
Example: CreateProductCommandHandler should create product
→ Arrange: CreateProductCommand with name, price
→ Act: Send via MediatR
→ Assert: Product persisted, ID returned, CreatedAt setAbout this skill
Your .NET API compiles, runs on your machine, and then fails in production. JWTs return 401 even though the token looks valid. EF Core throws "a second operation was started on this context". A list endpoint pulls 40,000 rows through Include chains. Native AOT publish breaks because of a library you picked months ago. This skill builds and reviews ASP.NET Core minimal API services with those failure points designed out from the first scaffold.
What it does
- Scaffolds minimal API services as clean-architecture or vertical-slice layouts, sized to the service. You get no repository-over-DbContext ceremony for a 6-endpoint CRUD API.
- Writes EF Core mappings with IEntityTypeConfiguration, concurrency tokens, decimal precision, and read queries that project to DTOs with AsNoTracking and split queries where they are needed.
- Implements CQRS handlers with MediatR or plain injected handler classes, with pipeline behavior ordering for validation, logging, and transactions.
- Configures JWT bearer auth against an Authority with correct audience validation, claim mapping, space-separated scope checks, and a secure-by-default fallback policy.
- Checks Native AOT feasibility before you commit: JSON source generation, the slim builder, and which dependencies such as EF Core and reflection-scanning libraries block it.
- Reviews pasted Program.cs, endpoints, or DbContext code and ranks findings by production impact.
What you get
Working C# for endpoints, handlers, EF configuration, and Program.cs wiring, with stated assumptions and version-sensitive items flagged. For reviews, you get a ranked list of issues, each with the fix.
Best for
Backend developers and small teams building or maintaining ASP.NET Core APIs and microservices on .NET 8 or later, especially teams moving from controllers to minimal APIs or considering Native AOT.
SKILL.md preview
---
name: dotnet-minimal-api-services
… (sign up to view the full skill)More development skills
View all Development skills →Production Go: Concurrency, Services & Profiling
Production Go code with leak-free goroutines, context-correct services, wrapped errors, and pprof-driven performance fixes.
Backend Feature Build Plan
Builds one backend feature through gates: confirmed requirements, one-screen design, small tested slices, and a deploy/rollback handoff.
Consistent, Safe Application Logging
Adds structured, correlated logs with useful severity, safe fields, bounded volume, and targeted verification.
Reliable WebSocket Messaging and Scaling
Build WebSocket and Socket.IO systems with authorized rooms, reconnect recovery, bounded queues, and multi-node presence.