dotnet-expert · .NET 8+ · 2026-07-17 · sha256 b93c7eca84fa4047
dotnet-expert .NET 8+A
Immutable. This exact content never changes and is served at /api/v1/blob/b93c7eca84fa4047.
---
name: dotnet-expert
description: >
Use when working with C# or .NET code: implementing features in ASP.NET Core,
Entity Framework Core, or Blazor; writing async/await patterns; designing with
Clean Architecture, CQRS, or DDD; reviewing .NET performance; creating NuGet packages.
license: MIT
compatibility: opencode
metadata:
stack: csharp, dotnet, aspnetcore, efcore, blazor
version: .NET 8+
---
# .NET / C# Expert Skill
You are an expert C# and .NET architect with deep knowledge of the entire .NET ecosystem.
## When to Use
Activate this skill when the user:
- Writes or reviews C# code
- Works with ASP.NET Core (minimal APIs, controllers, middleware)
- Uses Entity Framework Core (migrations, queries, relationships)
- Implements async patterns, TPL, or parallel programming
- Designs with Clean Architecture, DDD, CQRS (MediatR)
- Creates or consumes APIs in .NET
- Works with Blazor Server or WebAssembly
- Builds Azure functions or .NET Worker Services
## Core C# Standards
### Async / Await
```csharp
// ✅ Correct
public async Task<Result<User>> GetUserAsync(int id, CancellationToken ct)
{
var user = await _repository.FindAsync(id, ct);
return user is null ? Result.Fail("Not found") : Result.Ok(user);
}
// ❌ Never use
var result = SomeMethod().Result; // Deadlock risk
SomeMethod().Wait(); // Deadlock risk
```
### Null Safety
```csharp
// ✅ Enable nullable reference types in .csproj
<Nullable>enable</Nullable>
// ✅ Use pattern matching
if (user is { Email: not null } u) { ... }
// ✅ Use null-conditional and null-coalescing
var name = user?.Name ?? "Anonymous";
```
### Records and Value Types
```csharp
// ✅ Use records for DTOs and value objects
public record CreateUserRequest(string Name, string Email);
public record UserId(Guid Value);
// ✅ Use init-only properties for immutable data
public sealed class UserConfig
{
public required string Name { get; init; }
public required string Email { get; init; }
}
```
### Dependency Injection
```csharp
// ✅ Constructor injection, never service locator
public sealed class OrderService(
IOrderRepository repository,
ILogger<OrderService> logger,
TimeProvider timeProvider)
{
// Use primary constructor syntax (.NET 8+)
}
```
## ASP.NET Core Patterns
### Minimal API (Preferred for .NET 8+)
```csharp
// ✅ Group related endpoints
var users = app.MapGroup("/api/users").RequireAuthorization();
users.MapGet("/", GetAllUsers);
users.MapGet("/{id:int}", GetUserById);
users.MapPost("/", CreateUser);
// ✅ Use typed results
static async Task<Results<Ok<UserDto>, NotFound>> GetUserById(
int id, IUserService svc, CancellationToken ct) =>
await svc.GetAsync(id, ct) is { } user
? TypedResults.Ok(user.ToDto())
: TypedResults.NotFound();
```
### Middleware
```csharp
// ✅ Use IMiddleware for DI support
public sealed class CorrelationMiddleware(RequestDelegate next) : IMiddleware
{
public async Task InvokeAsync(HttpContext ctx, RequestDelegate _next)
{
ctx.Response.Headers["X-Correlation-ID"] =
ctx.Request.Headers.TryGetValue("X-Correlation-ID", out var v)
? v.ToString()
: Guid.NewGuid().ToString("N");
await next(ctx);
}
}
```
## Entity Framework Core
### Query Best Practices
```csharp
// ✅ Use AsNoTracking for read-only queries
var users = await context.Users
.AsNoTracking()
.Where(u => u.IsActive)
.Select(u => new UserDto(u.Id, u.Name)) // Project early
.ToListAsync(ct);
// ✅ Avoid N+1 — use Include or split queries
var orders = await context.Orders
.Include(o => o.Items)
.AsSplitQuery()
.ToListAsync(ct);
// ❌ Never load then filter in memory
var all = await context.Users.ToListAsync();
var active = all.Where(u => u.IsActive); // Loads entire table!
```
## Clean Architecture Layout
```
src/
├── Domain/ # Entities, Value Objects, Domain Events, Interfaces
├── Application/ # Use Cases, Commands, Queries, DTOs, Validators
├── Infrastructure/ # EF Core, External APIs, Email, File Storage
└── API/ # Controllers/Endpoints, Middleware, Filters
```
## Testing Standards
```csharp
// ✅ xUnit + FluentAssertions + Testcontainers
public sealed class UserServiceTests : IAsyncLifetime
{
private readonly PostgreSqlContainer _db = new PostgreSqlBuilder().Build();
[Fact]
public async Task CreateUser_WithValidData_ReturnsCreatedUser()
{
// Arrange
var sut = CreateSut();
var request = new CreateUserRequest("Alice", "alice@example.com");
// Act
var result = await sut.CreateAsync(request, CancellationToken.None);
// Assert
result.Should().BeSuccess();
result.Value.Name.Should().Be("Alice");
}
}
```
## Quality Checklist
Before completing .NET work, verify:
- [ ] All async methods use `CancellationToken`
- [ ] No `.Result` or `.Wait()` calls
- [ ] `IDisposable` types are properly disposed (using statements)
- [ ] Nullable reference types enabled and handled
- [ ] Logging uses `ILogger<T>`, not `Console.Write`
- [ ] Secrets not in config files — use `IConfiguration` / environment vars
- [ ] EF queries use `.AsNoTracking()` for reads
- [ ] Tests exist for all new public methods