Claude Code .NET Core 10 Setup: Web API Conventions

.net
.net core

The third time Claude Code suggested I scaffold a controller, I stopped typing and started writing a CLAUDE.md file.

That's the problem with AI coding assistants without context: they default to whatever Microsoft's documentation says. Ask Claude to add a new entity and it reaches for [ApiController], IActionResult, Data Annotations — the full textbook setup — regardless of the conventions your team has spent years building. That's not Claude being wrong. It's Claude not knowing you.

Here's how I set up a .NET Core 10 Web API project so Claude Code actually understands it. By the end, you'll have a project brain in CLAUDE.md, specialized agents for focused tasks, a slash command that scaffolds a full EF Core entity in one shot, and a lean strategy for keeping token usage in check. No theory. Just the setup.


Why Context Is Everything

Without context, Claude Code gives you textbook .NET. Ask it to add a service and it suggests a controller class with IActionResult and [HttpGet] attributes. Ask it to query the database and it writes synchronous LINQ. It'll recommend setting up AddDbContext with a full options builder — on a project where that's been in Program.cs for two years.

With context, everything changes. Claude knows you use minimal API handlers, that you return Result<T> for all domain operations, that CancellationToken flows from every endpoint down to every repository call. It knows this because you told it — once, explicitly, in a file it reads at the start of every session.

The payoff is precision. Scaffolding that doesn't need to be rewritten. Convention enforcement without nagging. Code reviews that check against your rules, not Microsoft's defaults.

One afternoon of setup. Every session after that is faster. That's the 75% easier rule I apply to every tool I build — invest once, collect forever.


CLAUDE.md — The Project Brain

CLAUDE.md is a plain markdown file at the root of your repo. Claude Code reads it automatically at the start of every session. Check it into source control and every developer on the team — and every AI session — starts from the same baseline.

Here's exactly what I put in mine.

Project Structure

## Project Structure

src/
  MyApi/
    Endpoints/       <- minimal API endpoint files, one per domain entity
    Domain/          <- entities, value objects, domain events
    Infrastructure/  <- EF Core DbContext, repositories, migrations
    Features/        <- optional: vertical slice folders
  MyApi.Tests/
    Unit/
    Integration/
tests/
MyApi.sln

No Controllers/ folder. That's intentional — make it explicit so Claude notices the absence and understands the reason.

Tech Stack

## Tech Stack

- Runtime: .NET 10, C# 13
- ORM: EF Core 9 via Npgsql.EntityFrameworkCore.PostgreSQL
  (MS SQL variant: Microsoft.EntityFrameworkCore.SqlServer)
- Mediator: MediatR (CQRS pattern)
- Validation: FluentValidation
- Logging: Serilog
- Hosting: WebApplication.CreateBuilder (not the legacy IHostBuilder pattern)

Pinning the packages and calling out the hosting model matters. Once Claude knows you're on WebApplication.CreateBuilder, it stops suggesting the old IHostBuilder / Startup.cs pattern that still turns up in half the tutorials online.

Coding Conventions

This is the section that earns its weight. Be explicit. Use negatives. Don't make Claude guess.

## Coding Conventions

### Naming
- Async methods: always suffix with Async (e.g. GetOrderAsync, not GetOrder)
- Interfaces: I-prefix (IOrderRepository, not OrderRepository as interface name)
- Entity classes: PascalCase, match the DB table name exactly

### Async Patterns
- Always async/await — never .Result, .Wait(), or .GetAwaiter().GetResult()
- CancellationToken must be passed from endpoint handler through to repository

### Result Types
- Return Result<T> or OneOf<T, Error> for all domain operations
- ❌ Do not throw exceptions for domain errors (validation failures, not-found, etc.)
- Exceptions are for infrastructure failures only (DB unreachable, network timeout)

### Minimal API Handlers
- ❌ Do not use [ApiController] or ControllerBase
- ❌ Do not use IActionResult or ActionResult<T>
- Use Results<T> from Microsoft.AspNetCore.Http
- Group endpoints via IEndpointRouteBuilder extension methods
- One endpoint file per aggregate root

### EF Core
- Always use explicit IEntityTypeConfiguration<T> in Infrastructure/Configurations/
- ❌ Do not use Data Annotations on entity classes
- Read queries must use AsNoTracking()
- Never access DbContext outside of Infrastructure/

The markers aren't decoration. Claude treats hard negatives as constraints. Less correction, fewer repeated mistakes.

Testing Approach

## Testing

- Integration tests hit a real PostgreSQL instance via Testcontainers — no mocking the DB
- Unit tests cover domain logic and FluentValidation validators only
- Stack: xUnit, FluentAssertions, Testcontainers.PostgreSql
- Test naming: MethodName_StateUnderTest_ExpectedBehaviour
  Example: CreateOrder_WithInvalidCustomerId_ReturnsValidationError

Common Commands

## Commands

Build:      dotnet build src/MyApi/MyApi.csproj
Test:       dotnet test --no-build --verbosity minimal
Run:        dotnet run --project src/MyApi
Migration:  dotnet ef migrations add <Name> --project src/MyApi --startup-project src/MyApi
DB update:  dotnet ef database update --project src/MyApi

Claude Code runs commands on your behalf. If you don't specify them, it guesses — and usually guesses wrong about your project layout.

What NOT To Do

The most important block in CLAUDE.md. A hard-constraint list Claude Code takes seriously.

## Never Do This

- ❌ Do not scaffold controllers or use ControllerBase
- ❌ Do not use IActionResult or ActionResult<T> — use Results<T> from minimal APIs
- ❌ Do not use Data Annotations for EF entity mapping
- ❌ Do not call .Result or .Wait() on async methods
- ❌ Do not create or resolve a DbContext outside of Infrastructure/
- ❌ Do not write synchronous database queries
- ❌ Do not add new NuGet packages without asking first

This same approach transfers to other contexts — I use it for my setup for running Claude Code agents on a Ghost publishing workflow too. The pattern is identical: define the rules once, enforce them everywhere.


Custom Agents for Specialized Work

An agent in Claude Code is a specialized subagent with a constrained tool set and its own system prompt. You define it in a markdown file under .claude/agents/<name>.md. Claude invokes it when the task fits its domain — or you call it by name directly.

The value for .NET projects is scope isolation. A general Claude session has access to every tool and no particular focus. A db-migration agent only reads EF Core configuration files and only runs dotnet ef commands. It can't wander into your endpoint layer or propose a refactor when all you wanted was a migration.

Here's the agent file:

---
name: db-migration
description: EF Core migration specialist. Reads entity configs, generates migrations, updates the DB.
tools: Read, Grep, Glob, Bash
---

You are an EF Core migration agent for a .NET Core 10 Web API project.

Rules:
- Only read files under src/MyApi/Infrastructure/ and src/MyApi/Domain/
- When adding a migration, always run:
  dotnet ef migrations add <Name> --project src/MyApi --startup-project src/MyApi
- After adding a migration, read the generated migration file and confirm
  the Up/Down methods look correct before updating the database
- Never modify endpoint files or service registrations
- Never suggest changes outside the Infrastructure/ layer

You can build a code-reviewer agent the same way — reads a PR diff, checks it against your CLAUDE.md conventions, flags violations, touches nothing. Focused agents stay out of each other's way and keep the main context clean.


Slash Commands That Scaffold Boilerplate Instantly

Skills — slash commands — are markdown files in .claude/commands/ that become /command-name shortcuts in Claude Code. They're mini-prompts with arguments. Run /new-entity Product and Claude executes every step in the file, substituting Product wherever $ARGUMENTS appears.

Scaffolding an EF Core entity correctly — entity class, IEntityTypeConfiguration, repository interface, implementation, initial migration — takes 10 to 15 minutes by hand and is error-prone. One typo in a configuration, one missed AsNoTracking(), and you've introduced a bug that won't surface until load testing. A skill does it in one command, every time, following your conventions exactly.

Here's the full .claude/commands/new-entity.md:

---
name: new-entity
description: Scaffolds a new EF Core entity with configuration, repository interface,
             repository implementation, and an initial migration.
---

Create a new domain entity called $ARGUMENTS for this .NET Core 10 Web API project.

Follow these steps in order:

1. Create src/MyApi/Domain/$ARGUMENTS.cs — a plain C# class with:
   - public int Id { get; private set; }
   - Private setters for all properties
   - A static factory method Create(...) that validates inputs and returns the entity

2. Create src/MyApi/Infrastructure/Configurations/$ARGUMENTSConfiguration.cs
   implementing IEntityTypeConfiguration<$ARGUMENTS>.
   Map all properties explicitly. No Data Annotations.

3. Register the configuration in AppDbContext.OnModelCreating if not already
   using ApplyConfigurationsFromAssembly.

4. Create src/MyApi/Infrastructure/Repositories/I$ARGUMENTSRepository.cs
   Interface with: GetByIdAsync, AddAsync, UpdateAsync, DeleteAsync.
   All methods take a CancellationToken parameter.

5. Create src/MyApi/Infrastructure/Repositories/$ARGUMENTSRepository.cs
   Implement the interface using the injected AppDbContext.
   Read queries must use AsNoTracking().

6. Run: dotnet ef migrations add $ARGUMENTSInit --project src/MyApi --startup-project src/MyApi

7. Report the files created and paste the migration output.

Claude isn't guessing your conventions here. You encoded them once — they execute perfectly every time. A junior who joined last week produces the same entity structure as the person who designed the original architecture. That's the point.


Keeping Token Usage Lean

The naive approach — letting Claude grep blindly through your project — burns tokens fast and returns noisy results. On a mid-sized .NET codebase with hundreds of files, most of them in bin/ and obj/, that's expensive. Three tools fix it.

Repomix (npx repomix) packs your entire codebase into a single structured XML file optimized for AI consumption. Feed it to Claude at the start of a session when you need full-project awareness — architecture questions, large refactors, onboarding a new agent. Skip the noise with --ignore flags:

npx repomix --ignore 'bin/**,obj/**,**/Migrations/**,**/*.user' --output repomix-context.xml

Right tool for "understand my whole project" moments. For anything more targeted, it's overkill.

The built-in Explore subagent in Claude Code is a fast, read-only agent for targeted lookups. Find where UserRepository is implemented. List every file that references IOrderService. A fraction of the tokens that loading the full repo would cost, and it keeps results out of the main context window until Claude actually needs them. Use this for mid-session file hunts.

mcp-server-filesystem is the official MCP server for structured filesystem access. Claude gets the ability to list directory trees and read specific files on demand — file contents only enter the context when explicitly requested. The right default for large monorepos or sessions focused on a single feature area.

The sweet spot for a mid-to-large .NET project:

  1. CLAUDE.md — conventions always loaded, zero extra tokens per session
  2. Repomix — full-repo context at session start, when you actually need it
  3. Explore subagent — targeted symbol and file lookups mid-session
  4. mcp-server-filesystem — lazy file loading for large repos and focused sessions

What You Actually Get

One afternoon of setup. Then Claude Code knows your project structure, your full tech stack, every naming convention, every async rule, every EF Core constraint, and what it must never do. Custom agents handle focused tasks without bleeding into unrelated layers. The new-entity skill scaffolds a complete EF Core stack in seconds, following your conventions every time.

Sessions get faster. Reviews become genuinely useful. Junior developers are guided by the same rules encoded in CLAUDE.md — no tribal knowledge required, no onboarding conversation about "we don't use controllers here."

The AI stops fumbling around your project and starts operating like a developer who's been on the team for months. That's the compounding return on one afternoon.

If you've built a CLAUDE.md for your own .NET project that goes further than what I've covered here, I'd genuinely like to see it. Drop it in the comments or reach out directly. The more these setups get shared, the faster we all move.

Read more