Claude Code for C# / .NET — ASP.NET Core, EF Core & xUnit Workflows

C# and .NET power the world's largest enterprise systems. This guide shows how to configure Claude Code for idiomatic .NET development — minimal APIs, Entity Framework Core migrations, xUnit testing, and clean architecture patterns — using CLAUDE.md and automated hooks.

CLAUDE.md Template for .NET Solutions

Add this to your solution root. Claude reads it on every startup and generates code that matches your conventions immediately.

# CLAUDE.md — .NET Solution

## Build & test commands
- dotnet build                              # Build all projects
- dotnet test                               # Run all tests
- dotnet test tests/MyApp.Tests -v minimal  # Unit tests only
- dotnet ef migrations add <Name> --project Infrastructure --startup-project Api
- dotnet ef database update --project Infrastructure --startup-project Api

## Solution structure
- src/MyApp.Api          — ASP.NET Core minimal API, middleware, endpoints
- src/MyApp.Domain       — Entities, value objects, domain services, interfaces
- src/MyApp.Infrastructure — EF Core DbContext, repositories, external services
- tests/MyApp.Tests      — xUnit + Moq + FluentAssertions

## C# conventions
- Target: net9.0
- Nullable reference types: ENABLED — never use ! or default! to suppress
- Required: use 'required' keyword for non-optional properties
- DTOs: use C# records with init setters
- No Newtonsoft.Json — use System.Text.Json with source generators
- Async: all I/O methods must be async. No .Result or .Wait()
- Logging: ILogger<T> via DI — no Console.WriteLine in production code
- Exceptions: custom exception types in Domain; catch only at boundary handlers

## EF Core
- Provider: Npgsql (PostgreSQL)
- Migrations must be zero-downtime (expand/contract for renames)
- No lazy loading — use explicit Include() or projection queries
- Use AsNoTracking() for read-only queries

Automated Hooks for .NET

# .claude/settings.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "dotnet build --no-restore 2>&1 | grep -E 'error|warning|Build' | head -30"
          },
          {
            "type": "command",
            "command": "dotnet test --no-build --filter Category=Unit 2>&1 | tail -25"
          }
        ]
      }
    ]
  }
}
Use --no-build on the test hook so you only pay compile cost once per save, not twice. The build hook runs first and catches compile errors; the test hook reuses the compiled output.

C# / .NET Conventions for Claude Code

AreaConvention to specify in CLAUDE.mdWhy it matters
DTOsUse records with required init: record CreateOrderRequest(required string ProductId, ...)Immutable by default, structural equality, null-safety enforced by compiler
Error handlingDomain exceptions + Result<T,E> for expected failures; don't use exceptions for flow controlPrevents catching broad Exception in callers; forces explicit error handling
DI lifetimeRepositories = Scoped; HttpClient = Transient via IHttpClientFactory; caches = SingletonPrevents captive dependency bug (singleton holding scoped service)
EF queriesUse .AsNoTracking() for reads; explicit .Include() for related data; no N+1Avoids silent change-tracking overhead on read-heavy endpoints
CancellationAll async methods accept CancellationToken and pass it to EF/HttpClient callsEnables request cancellation on client disconnect; required for resilient APIs
JSONSystem.Text.Json with JsonSerializerContext source generation; snake_case property namesAOT-compatible, no reflection overhead, consistent API contract

ASP.NET Core Endpoint Prompts

Minimal API POST endpoint with validation

# Prompt
"Add POST /api/v1/orders endpoint.
- Validate CreateOrderRequest with FluentValidation (ProductId required, Quantity 1-100)
- Use IOrderRepository injected via DI
- Return 201 with Location header and OrderId on success
- Return 422 ProblemDetails with field errors on validation failure
- Return 409 if order with same idempotency key already exists
- Include xUnit test with WebApplicationFactory covering all 3 status codes"

Entity Framework Core migration

# Prompt
"Add a new nullable 'ShippingAddress' column to Orders table.
Migration must be zero-downtime:
1. Migration 1: add nullable column
2. Application code updated to write new column (backfill on save)
3. Migration 2 (future): make NOT NULL after backfill complete
Generate only Migration 1 for now."

Background service (IHostedService)

# Prompt
"Add an IHostedService that processes orders from a channel.
- ExecuteAsync loop reads from Channel<Order> with CancellationToken
- Batches orders every 5s or when batch reaches 50 items
- Logs structured events with ILogger on success and failure
- Stops cleanly on app shutdown (CancellationToken.IsCancellationRequested)"

xUnit Testing Patterns

Test typePatternClaude Code prompt hint
Unit test (pure logic)xUnit [Fact]/[Theory] + FluentAssertions"Write unit tests for OrderService.CalculateTotal(). Cover: empty cart, single item, quantity discount at 10+, negative price guard."
Controller / endpoint testWebApplicationFactory + HttpClient"Write integration tests for POST /api/v1/orders using WebApplicationFactory. Mock IOrderRepository with Moq."
EF Core integration testTestcontainers for PostgreSQL"Write EF integration test using Testcontainers.PostgreSql. Migrate schema, seed fixture data, assert query returns expected rows."
Snapshot testVerify.Xunit for JSON response shape"Add Verify snapshot tests for the GetOrder response — any field addition/removal should require snapshot update approval."

Clean Architecture with Claude Code

# Prompt for full slice
"Scaffold a full vertical slice for 'Create Product':
- Domain: Product entity + IProductRepository interface in Domain project
- Infrastructure: ProductRepository implementation + EF config in Infrastructure
- API: POST /api/v1/products minimal API endpoint in Api project
- Tests: unit test for domain logic + integration test for the endpoint
Follow the existing patterns in the codebase (nullable enabled, records for DTOs)."
Calculate Claude API costs for your .NET AI features
Try the Claude Cost Calculator →

Integrating Claude API into ASP.NET Core

# Prompt
"Add a service that calls the Anthropic Claude API to summarize
customer feedback. Use HttpClient via IHttpClientFactory.
Add retry policy (Polly: 3 retries, exponential backoff, jitter).
Inject IClaudeSummaryService via DI with Scoped lifetime.
Include a unit test mocking the HttpClient."
// Claude generates — AnthropicService.cs
public class ClaudeSummaryService(IHttpClientFactory factory, ILogger<ClaudeSummaryService> logger) : IClaudeSummaryService
{
    public async Task<string> SummariseFeedbackAsync(string text, CancellationToken ct = default)
    {
        var client = factory.CreateClient("Anthropic");
        var payload = new
        {
            model = "claude-sonnet-4-6",
            max_tokens = 256,
            messages = new[] { new { role = "user", content = $"Summarise this customer feedback in 2 sentences:\n\n{text}" } }
        };
        var response = await client.PostAsJsonAsync("/v1/messages", payload, ct);
        response.EnsureSuccessStatusCode();
        var result = await response.Content.ReadFromJsonAsync<AnthropicResponse>(cancellationToken: ct);
        logger.LogInformation("Summarised feedback, input_tokens={In}", result!.Usage.InputTokens);
        return result.Content[0].Text;
    }
}

5 .NET Tips for Claude Code Users

Related Guides

Java & Spring Boot →  |  Docker containers →  |  Database & EF migrations →  |  Testing workflows →  |  Hook patterns →

📘 Free: 5 sample Claude Code prompts · plus the £3 pack with 25 moreSee the free 5 →