Use when writing C#/.NET backend code — ASP.NET Core APIs (minimal APIs or MVC), workers, EF Core data access. Builder playbook: layout, DI lifetimes, async/cancellation, EF Core discipline, options pattern, error contract, testing shape. Complements csharp-reviewer.
Scanned 9/1/2026
Install to Claude Code
npx -y skills add jdanigo/hydraia --skill dotnet-patterns --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Dotnet Patterns?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jdanigo-dotnet-patterns)More formats (shields.io, HTML) on the badges page.
---
name: dotnet-patterns
description: Use when writing C#/.NET backend code — ASP.NET Core APIs (minimal APIs or MVC), workers, EF Core data access. Builder playbook: layout, DI lifetimes, async/cancellation, EF Core discipline, options pattern, error contract, testing shape. Complements csharp-reviewer.
---
# C#/.NET Backend Patterns
## Layout
- Vertical slices (feature folders: endpoint + handler + models per feature) for API-shaped services; full Clean Architecture layering only when the domain genuinely warrants it — record the choice as an ADR either way.
- Minimal APIs for small/medium surfaces; MVC controllers when filters/model-binding complexity earns it.
## DI lifetimes (the classic traps)
- Scoped services must NEVER be captured by singletons (captive dependency — resolve via `IServiceScopeFactory` inside the singleton instead).
- `HttpClient` via `IHttpClientFactory`, never `new HttpClient()` per call (socket exhaustion).
- Register by interface at the boundary you intend to fake in tests; concrete elsewhere is fine.
## Async all the way
- No `.Result`/`.Wait()`/`GetAwaiter().GetResult()` on async paths — deadlock and threadpool starvation fuel.
- `CancellationToken` flows from the endpoint through every service and EF call: `await db.Orders.ToListAsync(ct)`. Endpoints accept it; libraries propagate it.
- `ValueTask` only where profiling justifies it; default is `Task`.
## EF Core discipline
- Reads: `AsNoTracking()` by default; project to DTOs with `Select` — never return entities from queries that don't update.
- No lazy-loading proxies: load explicitly (`Include`) so every query's shape is visible — N+1 becomes impossible to miss.
- Migrations: one migration per change, reviewed like code; destructive column changes follow expand-contract (see db-optimization skill).
- Transactions explicit where multi-write consistency matters (`ExecutionStrategy` + `TransactionScope`-free patterns with `BeginTransactionAsync`).
## Config — options pattern
- `IOptions<T>` with `ValidateDataAnnotations().ValidateOnStart()` — invalid config fails the boot, not the first request.
- Secrets from user-secrets/KeyVault/env — never appsettings committed values.
## Error contract
- `ProblemDetails` (RFC 9457) via `AddProblemDetails()`; exceptions mapped centrally (exception handler middleware), typed domain exceptions → status codes. No try/catch per controller.
- Nullable reference types ON project-wide; warnings as errors for new code.
## Testing shape
- xUnit; unit tests fake at the interface boundary.
- `WebApplicationFactory<Program>` for integration tests over the real pipeline (routing, filters, serialization) with a test database (Testcontainers where Docker exists).
- Snapshot the API surface where the contract matters (Verify/ApiApprovals) so drift is a failing test.

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!