
Dotnet Patterns
Keep new and refactored C# aligned with idiomatic records, DI, async/await, and ASP.NET Core service design.
Overview
Dotnet-patterns is an agent skill most often used in Build (also Ship review) that applies idiomatic C#, dependency injection, async/await, and ASP.NET Core conventions when writing or reviewing .NET code.
Install
npx skills add https://github.com/affaan-m/everything-claude-code --skill dotnet-patternsWhat is this skill?
- Promotes immutability with records and init-only DTOs over mutable public setters
- Enforces explicit nullability, access modifiers, and constructor injection in services
- Covers async/await and robust ASP.NET Core service layering conventions
- Applies when writing, reviewing, or refactoring existing .NET applications
Adoption & trust: 3.2k installs on skills.sh; 210k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are building or refactoring C# without a shared standard, so DTOs, nullability, and DI drift and reviews become debates about style instead of behavior.
Who is it for?
Solo builders shipping ASP.NET Core APIs or .NET services who want the agent to mirror team-grade C# conventions from the first commit.
Skip if: Greenfield teams on non-.NET stacks, or when you only need one-off scripts with no interest in long-term .NET architecture rules.
When should I use this skill?
Writing new C# code, reviewing C# code, refactoring existing .NET applications, or designing service architectures with ASP.NET Core.
What do I get? / Deliverables
New and refactored C# follows explicit, immutable-friendly .NET patterns that are easier to test, extend, and pass code review.
- C# code and refactors aligned to documented .NET patterns
- Review comments grounded in immutability, DI, and async conventions
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
.NET application code and service architecture are authored in the Build phase; this skill is the canonical shelf for backend .NET work. Patterns cover server-side C#, repositories, and ASP.NET Core—not frontend UI or infra deploy scripts.
Where it fits
Scaffold order and payment services with constructor injection and sealed record types for money and line items.
Review a contributor PR for mutable entities and missing nullability annotations before merge.
Refactor a monolithic controller into application services while keeping async boundaries clear.
How it compares
Use as procedural .NET style guidance during authoring and review—not as a NuGet package, MCP server, or test runner.
Common Questions / FAQ
Who is dotnet-patterns for?
Indie and solo developers using Claude Code, Cursor, or Codex on C# and .NET backends who want consistent idioms without hiring a platform lead.
When should I use dotnet-patterns?
During Build when implementing ASP.NET Core services or domain models; during Ship when reviewing C# PRs; when refactoring legacy .NET apps before adding features.
Is dotnet-patterns safe to install?
It is documentation-style procedural knowledge with no required network or secret access; review the Security Audits panel on this Prism page before enabling in your agent.
SKILL.md
READMESKILL.md - Dotnet Patterns
# .NET Development Patterns Idiomatic C# and .NET patterns for building robust, performant, and maintainable applications. ## When to Activate - Writing new C# code - Reviewing C# code - Refactoring existing .NET applications - Designing service architectures with ASP.NET Core ## Core Principles ### 1. Prefer Immutability Use records and init-only properties for data models. Mutability should be an explicit, justified choice. ```csharp // Good: Immutable value object public sealed record Money(decimal Amount, string Currency); // Good: Immutable DTO with init setters public sealed class CreateOrderRequest { public required string CustomerId { get; init; } public required IReadOnlyList<OrderItem> Items { get; init; } } // Bad: Mutable model with public setters public class Order { public string CustomerId { get; set; } public List<OrderItem> Items { get; set; } } ``` ### 2. Explicit Over Implicit Be clear about nullability, access modifiers, and intent. ```csharp // Good: Explicit access modifiers and nullability public sealed class UserService { private readonly IUserRepository _repository; private readonly ILogger<UserService> _logger; public UserService(IUserRepository repository, ILogger<UserService> logger) { _repository = repository ?? throw new ArgumentNullException(nameof(repository)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task<User?> FindByIdAsync(Guid id, CancellationToken cancellationToken) { return await _repository.FindByIdAsync(id, cancellationToken); } } ``` ### 3. Depend on Abstractions Use interfaces for service boundaries. Register via DI container. ```csharp // Good: Interface-based dependency public interface IOrderRepository { Task<Order?> FindByIdAsync(Guid id, CancellationToken cancellationToken); Task<IReadOnlyList<Order>> FindByCustomerAsync(string customerId, CancellationToken cancellationToken); Task AddAsync(Order order, CancellationToken cancellationToken); } // Registration builder.Services.AddScoped<IOrderRepository, SqlOrderRepository>(); ``` ## Async/Await Patterns ### Proper Async Usage ```csharp // Good: Async all the way, with CancellationToken public async Task<OrderSummary> GetOrderSummaryAsync( Guid orderId, CancellationToken cancellationToken) { var order = await _repository.FindByIdAsync(orderId, cancellationToken) ?? throw new NotFoundException($"Order {orderId} not found"); var customer = await _customerService.GetAsync(order.CustomerId, cancellationToken); return new OrderSummary(order, customer); } // Bad: Blocking on async public OrderSummary GetOrderSummary(Guid orderId) { var order = _repository.FindByIdAsync(orderId, CancellationToken.None).Result; // Deadlock risk return new OrderSummary(order); } ``` ### Parallel Async Operations ```csharp // Good: Concurrent independent operations public async Task<DashboardData> LoadDashboardAsync(CancellationToken cancellationToken) { var ordersTask = _orderService.GetRecentAsync(cancellationToken); var metricsTask = _metricsService.GetCurrentAsync(cancellationToken); var alertsTask = _alertService.GetActiveAsync(cancellationToken); await Task.WhenAll(ordersTask, metricsTask, alertsTask); return new DashboardData( Orders: await ordersTask, Metrics: await metricsTask, Alerts: await alertsTask); } ``` ## Options Pattern Bind configuration sections to strongly-typed objects. ```csharp public sealed class SmtpOptions { public const string SectionName = "Smtp"; public required string Host { get; init; } public required int Port { get; init; } public required string Username { get; init; }