plugins/dotnet-blazor/skills/csharp-patterns/SKILL.md
Modern C# 13/15 patterns, LINQ mastery, async/await, records, pattern matching, .NET 10/11 features, and .NET AI integration
npx skillsauth add markus41/claude csharp-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
public sealed class OrderService(
IOrderRepository repo,
IPaymentGateway payments,
ILogger<OrderService> logger) : IOrderService
{
public async Task<OrderResult> ProcessAsync(CreateOrderCommand cmd, CancellationToken ct)
{
logger.LogInformation("Processing order for customer {CustomerId}", cmd.CustomerId);
var order = await repo.CreateAsync(cmd, ct);
var payment = await payments.ChargeAsync(order.Total, ct);
return new OrderResult(order.Id, payment.TransactionId);
}
}
public sealed class EmailOptions
{
public const string SectionName = "Email";
public required string SmtpHost { get; init; }
public required int SmtpPort { get; init; }
public required string FromAddress { get; init; }
}
// Registration
builder.Services.Configure<EmailOptions>(builder.Configuration.GetSection(EmailOptions.SectionName));
// Usage
public sealed class EmailService(IOptions<EmailOptions> options)
{
private readonly EmailOptions _config = options.Value;
}
public abstract record Result<T>
{
public sealed record Success(T Value) : Result<T>;
public sealed record Failure(string Error) : Result<T>;
public TOut Match<TOut>(Func<T, TOut> onSuccess, Func<string, TOut> onFailure) =>
this switch
{
Success s => onSuccess(s.Value),
Failure f => onFailure(f.Error),
_ => throw new InvalidOperationException()
};
}
// Usage
public async Task<Result<OrderDto>> CreateOrderAsync(CreateOrderCommand cmd)
{
if (await _repo.ExistsAsync(cmd.Sku))
return new Result<OrderDto>.Failure("Duplicate SKU");
var order = await _repo.CreateAsync(cmd);
return new Result<OrderDto>.Success(order.ToDto());
}
public interface IRepository<T> where T : class
{
Task<T?> GetByIdAsync(int id, CancellationToken ct = default);
Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec, CancellationToken ct = default);
Task<T> AddAsync(T entity, CancellationToken ct = default);
Task UpdateAsync(T entity, CancellationToken ct = default);
Task DeleteAsync(T entity, CancellationToken ct = default);
}
public interface ISpecification<T>
{
Expression<Func<T, bool>>? Criteria { get; }
List<Expression<Func<T, object>>> Includes { get; }
Expression<Func<T, object>>? OrderBy { get; }
int? Take { get; }
int? Skip { get; }
}
public static class Guard
{
public static T NotNull<T>(T? value, [CallerArgumentExpression(nameof(value))] string? name = null)
where T : class =>
value ?? throw new ArgumentNullException(name);
public static string NotEmpty(string? value, [CallerArgumentExpression(nameof(value))] string? name = null) =>
string.IsNullOrWhiteSpace(value)
? throw new ArgumentException("Cannot be empty", name)
: value;
}
public static class EnumerableExtensions
{
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
{
var batch = new List<T>(size);
foreach (var item in source)
{
batch.Add(item);
if (batch.Count == size)
{
yield return batch;
batch = new List<T>(size);
}
}
if (batch.Count > 0) yield return batch;
}
}
// Parallel execution
var (users, products) = await (GetUsersAsync(ct), GetProductsAsync(ct));
// Async streams
await foreach (var item in GetItemsAsync(ct))
{
await ProcessAsync(item, ct);
}
// Channel for producer/consumer
var channel = Channel.CreateBounded<WorkItem>(100);
// Producer
await channel.Writer.WriteAsync(new WorkItem(...), ct);
// Consumer
await foreach (var item in channel.Reader.ReadAllAsync(ct))
{
await HandleAsync(item, ct);
}
// Extension properties, methods, and static members
public static extension StringExtensions for string
{
public bool IsNullOrEmpty => string.IsNullOrEmpty(this);
public string Reversed => new(this.Reverse().ToArray());
}
// Usage: "hello".Reversed → "olleh"
public class Person
{
// 'field' keyword accesses the auto-generated backing field
public string Name
{
get => field;
set => field = value?.Trim() ?? throw new ArgumentNullException(nameof(value));
}
}
// Only assigns if left side is not null
object?.Property = value;
list?.Add(item);
// Microsoft.Extensions.AI - unified AI abstraction
using Microsoft.Extensions.AI;
// Register AI chat client (works with OpenAI, Azure OpenAI, Ollama, etc.)
builder.Services.AddChatClient(new AzureOpenAIClient(
new Uri(builder.Configuration["AI:Endpoint"]!),
new DefaultAzureCredential())
.GetChatClient("gpt-4o"));
// Use in services
public sealed class SmartSearchService(IChatClient chatClient)
{
public async Task<string> SummarizeAsync(string content, CancellationToken ct)
{
var response = await chatClient.GetResponseAsync(
$"Summarize this: {content}", cancellationToken: ct);
return response.Text;
}
}
// Semantic Kernel for AI orchestration
using Microsoft.SemanticKernel;
var kernel = Kernel.CreateBuilder()
.AddAzureOpenAIChatCompletion("gpt-4o", endpoint, credential)
.Build();
var result = await kernel.InvokePromptAsync(
"Analyze this order data: {{$input}}", new() { ["input"] = orderJson });
development
Enhanced plan-authoring skill with Pre-Writing context gathering, task metadata, non-TDD templates, Red Flags, telemetry, and an automated plan linter. Use when you have a spec or requirements for a multi-step task, before touching code.
tools
Documentation intelligence engine with graph-based API docs, algorithm library, and drift detection
tools
Ultraplan cloud planning — kick off a plan in the cloud from your terminal, review and revise in the browser, then execute remotely or send back to CLI
tools
--- name: mcp description: Configure MCP servers for Claude Code — stdio vs HTTP, authentication, Tools/Resources/Prompts distinction, channels (CI webhook, mobile relay, Discord bridge, fakechat), and cost of always-loaded tools. Use this skill whenever adding an MCP server, debugging connection issues, choosing between MCP Tools vs Prompts vs Resources, installing channel servers, or managing .mcp.json. Triggers on: "MCP server", "mcp config", "add Obsidian MCP", "install context7", "channels"