.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.

Development / Backenddotnetaspnet-core
by AgentVoltv1.0.0
Free to sign up · every skill included with AgentVolt Pro

Before & after

Same prompt, same agent — the only difference is the skill.

Prompt“Build a REST API for product management with Entity Framework”
Beforegeneric AI agent
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?
Afterwith .NET Minimal API Services with EF Core and CQRS
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 set

About 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

SKILL.md
---
name: dotnet-minimal-api-services

… (sign up to view the full skill)
Sign up to view, copy, and install the full skill

More development skills

View all Development skills →