
Csharp Dotnet
- 84 installs
- 19 repo stars
- Updated January 20, 2026
- miles990/claude-software-skills
Helps with ai & agent building tasks.
About
csharp-dotnet is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- csharp-dotnet
- AI & Agent Building
- AI-coding skill
Csharp Dotnet by the numbers
- 84 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #5,107 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/miles990/claude-software-skills --skill csharp-dotnetAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 84 |
|---|---|
| repo stars | ★ 19 |
| Last updated | January 20, 2026 |
| Repository | miles990/claude-software-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
C# & .NET
Overview
Modern C# and .NET development patterns including async programming, LINQ, and ASP.NET Core.
---
Modern C# Features
Records and Init-Only Properties
// Record type (immutable by default)
public record User(
string Id,
string Email,
string Name,
DateTime CreatedAt
);
// With-expressions for immutable updates
var user = new User("1", "test@example.com", "Test User", DateTime.UtcNow);
var updated = user with { Name = "Updated Name" };
// Record with validation
public record ValidatedUser
{
public required string Id { get; init; }
public required string Email { get; init; }
public required string Name { get; init; }
public ValidatedUser()
{
// Validation in constructor
}
// Custom validation
public static ValidatedUser Create(string email, string name)
{
if (!email.Contains("@"))
throw new ArgumentException("Invalid email");
return new ValidatedUser
{
Id = Guid.NewGuid().ToString(),
Email = email,
Name = name
};
}
}
// Init-only properties
public class Config
{
public required string ConnectionString { get; init; }
public int Timeout { get; init; } = 30;
}Pattern Matching
// Type patterns
public string Describe(object obj) => obj switch
{
string s => $"String of length {s.Length}",
int i when i > 0 => $"Positive integer: {i}",
int i => $"Non-positive integer: {i}",
IEnumerable<int> list => $"Integer list with {list.Count()} items",
null => "Null value",
_ => "Unknown type"
};
// Property patterns
public decimal CalculateDiscount(Order order) => order switch
{
{ Total: > 1000, Customer.IsPremium: true } => order.Total * 0.2m,
{ Total: > 1000 } => order.Total * 0.1m,
{ Customer.IsPremium: true } => order.Total * 0.05m,
_ => 0
};
// List patterns (C# 11)
public string DescribeList(int[] numbers) => numbers switch
{
[] => "Empty",
[var single] => $"Single element: {single}",
[var first, var second] => $"Two elements: {first}, {second}",
[var first, .. var middle, var last] => $"First: {first}, Last: {last}, Middle count: {middle.Length}",
};
// Positional patterns with deconstruct
public record Point(int X, int Y);
public string Quadrant(Point point) => point switch
{
(0, 0) => "Origin",
(> 0, > 0) => "First quadrant",
(< 0, > 0) => "Second quadrant",
(< 0, < 0) => "Third quadrant",
(> 0, < 0) => "Fourth quadrant",
(_, 0) or (0, _) => "On an axis"
};Nullable Reference Types
#nullable enable
public class UserService
{
// Non-nullable (compiler ensures not null)
public User GetUser(string id)
{
return _repository.Find(id)
?? throw new NotFoundException($"User {id} not found");
}
// Nullable return
public User? FindUser(string id)
{
return _repository.Find(id);
}
// Null handling
public string GetDisplayName(User? user)
{
// Null-conditional
var name = user?.Name;
// Null-coalescing
return user?.Name ?? "Anonymous";
// Null-coalescing assignment
// user ??= CreateDefaultUser();
}
// Null-forgiving operator (use sparingly)
public void ProcessUser(User? user)
{
// When you know it's not null but compiler doesn't
var name = user!.Name;
}
}
// Required members (C# 11)
public class RequiredUser
{
public required string Id { get; init; }
public required string Email { get; init; }
public string? Nickname { get; init; }
}---
Async Programming
using System.Threading.Tasks;
using System.Threading;
public class AsyncService
{
// Basic async method
public async Task<User> GetUserAsync(string id, CancellationToken ct = default)
{
var response = await _httpClient.GetAsync($"/users/{id}", ct);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<User>(ct)
?? throw new InvalidOperationException("Null response");
}
// Parallel execution
public async Task<IReadOnlyList<User>> GetUsersAsync(IEnumerable<string> ids)
{
var tasks = ids.Select(id => GetUserAsync(id));
return await Task.WhenAll(tasks);
}
// With error handling
public async Task<Result<User>> SafeGetUserAsync(string id)
{
try
{
var user = await GetUserAsync(id);
return Result<User>.Success(user);
}
catch (Exception ex)
{
return Result<User>.Failure(ex.Message);
}
}
// ValueTask for potentially synchronous operations
public ValueTask<User?> GetCachedUserAsync(string id)
{
if (_cache.TryGetValue(id, out var user))
{
return ValueTask.FromResult<User?>(user);
}
return new ValueTask<User?>(FetchAndCacheUserAsync(id));
}
// Async enumerable (IAsyncEnumerable)
public async IAsyncEnumerable<User> StreamUsersAsync(
[EnumeratorCancellation] CancellationToken ct = default)
{
var page = 1;
while (true)
{
var users = await FetchPageAsync(page, ct);
if (!users.Any()) yield break;
foreach (var user in users)
{
yield return user;
}
page++;
}
}
// Consuming async enumerable
public async Task ProcessAllUsersAsync()
{
await foreach (var user in StreamUsersAsync())
{
await ProcessUserAsync(user);
}
}
}
// Channels for producer-consumer
public class ChannelExample
{
private readonly Channel<Message> _channel = Channel.CreateBounded<Message>(100);
public async Task ProduceAsync(Message message)
{
await _channel.Writer.WriteAsync(message);
}
public async Task ConsumeAsync(CancellationToken ct)
{
await foreach (var message in _channel.Reader.ReadAllAsync(ct))
{
await ProcessMessageAsync(message);
}
}
}---
LINQ
using System.Linq;
public class LinqExamples
{
// Query syntax
public IEnumerable<User> GetActiveUsers(IEnumerable<User> users)
{
return from user in users
where user.IsActive
orderby user.Name
select user;
}
// Method syntax (more common)
public IEnumerable<string> GetActiveUserEmails(IEnumerable<User> users)
{
return users
.Where(u => u.IsActive)
.OrderBy(u => u.Name)
.Select(u => u.Email);
}
// Grouping
public IDictionary<string, List<User>> GroupByDomain(IEnumerable<User> users)
{
return users
.GroupBy(u => u.Email.Split('@')[1])
.ToDictionary(g => g.Key, g => g.ToList());
}
// Join
public IEnumerable<OrderWithUser> JoinOrdersWithUsers(
IEnumerable<Order> orders,
IEnumerable<User> users)
{
return orders.Join(
users,
order => order.UserId,
user => user.Id,
(order, user) => new OrderWithUser(order, user));
}
// Aggregate
public OrderStats GetOrderStats(IEnumerable<Order> orders)
{
return new OrderStats(
Count: orders.Count(),
Total: orders.Sum(o => o.Amount),
Average: orders.Average(o => o.Amount),
Max: orders.Max(o => o.Amount)
);
}
// SelectMany (flatten)
public IEnumerable<OrderItem> GetAllItems(IEnumerable<Order> orders)
{
return orders.SelectMany(o => o.Items);
}
// Complex pipeline
public IEnumerable<UserSummary> GetTopCustomers(
IEnumerable<User> users,
IEnumerable<Order> orders)
{
return orders
.GroupBy(o => o.UserId)
.Select(g => new
{
UserId = g.Key,
TotalSpent = g.Sum(o => o.Amount),
OrderCount = g.Count()
})
.OrderByDescending(x => x.TotalSpent)
.Take(10)
.Join(users, x => x.UserId, u => u.Id, (x, u) => new UserSummary(
u.Name,
x.TotalSpent,
x.OrderCount
));
}
}---
Dependency Injection
using Microsoft.Extensions.DependencyInjection;
// Service registration
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
// Transient - new instance each time
services.AddTransient<IEmailService, EmailService>();
// Scoped - one per request
services.AddScoped<IUserRepository, UserRepository>();
// Singleton - one instance for app lifetime
services.AddSingleton<ICacheService, RedisCacheService>();
// Factory registration
services.AddScoped<IDbConnection>(sp =>
{
var config = sp.GetRequiredService<IConfiguration>();
return new SqlConnection(config.GetConnectionString("Default"));
});
// Options pattern
services.Configure<EmailOptions>(configuration.GetSection("Email"));
return services;
}
}
// Constructor injection
public class UserService
{
private readonly IUserRepository _repository;
private readonly IEmailService _emailService;
private readonly ILogger<UserService> _logger;
public UserService(
IUserRepository repository,
IEmailService emailService,
ILogger<UserService> logger)
{
_repository = repository;
_emailService = emailService;
_logger = logger;
}
public async Task<User> CreateUserAsync(CreateUserRequest request)
{
_logger.LogInformation("Creating user {Email}", request.Email);
var user = new User(request.Email, request.Name);
await _repository.AddAsync(user);
await _emailService.SendWelcomeEmailAsync(user);
return user;
}
}
// Primary constructor (C# 12)
public class UserServiceNew(
IUserRepository repository,
IEmailService emailService,
ILogger<UserServiceNew> logger)
{
public async Task<User> CreateUserAsync(CreateUserRequest request)
{
logger.LogInformation("Creating user {Email}", request.Email);
// Use repository, emailService directly
return null!;
}
}---
Generic Patterns
// Generic repository
public interface IRepository<T> where T : class, IEntity
{
Task<T?> FindAsync(string id);
Task<IReadOnlyList<T>> FindAllAsync();
Task AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(string id);
}
public class Repository<T> : IRepository<T> where T : class, IEntity
{
private readonly DbContext _context;
public Repository(DbContext context)
{
_context = context;
}
public async Task<T?> FindAsync(string id)
{
return await _context.Set<T>().FindAsync(id);
}
public async Task<IReadOnlyList<T>> FindAllAsync()
{
return await _context.Set<T>().ToListAsync();
}
public async Task AddAsync(T entity)
{
await _context.Set<T>().AddAsync(entity);
await _context.SaveChangesAsync();
}
// ...
}
// Generic constraints
public class Service<TEntity, TId>
where TEntity : class, IEntity<TId>, new()
where TId : struct
{
// ...
}
// Covariance and contravariance
public interface IProducer<out T>
{
T Produce();
}
public interface IConsumer<in T>
{
void Consume(T item);
}---
Error Handling
// Result type pattern
public readonly struct Result<T>
{
public T? Value { get; }
public string? Error { get; }
public bool IsSuccess => Error is null;
private Result(T value)
{
Value = value;
Error = null;
}
private Result(string error)
{
Value = default;
Error = error;
}
public static Result<T> Success(T value) => new(value);
public static Result<T> Failure(string error) => new(error);
public TResult Match<TResult>(
Func<T, TResult> success,
Func<string, TResult> failure) =>
IsSuccess ? success(Value!) : failure(Error!);
}
// Custom exceptions
public class DomainException : Exception
{
public string Code { get; }
public DomainException(string code, string message)
: base(message)
{
Code = code;
}
}
public class ValidationException : DomainException
{
public IDictionary<string, string[]> Errors { get; }
public ValidationException(IDictionary<string, string[]> errors)
: base("VALIDATION_ERROR", "Validation failed")
{
Errors = errors;
}
}
// Exception filter middleware
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionMiddleware> _logger;
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (ValidationException ex)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsJsonAsync(new { ex.Code, ex.Errors });
}
catch (NotFoundException ex)
{
context.Response.StatusCode = 404;
await context.Response.WriteAsJsonAsync(new { ex.Code, ex.Message });
}
catch (Exception ex)
{
_logger.LogError(ex, "Unhandled exception");
context.Response.StatusCode = 500;
await context.Response.WriteAsJsonAsync(new { Code = "INTERNAL_ERROR" });
}
}
}---
Related Skills
- [[backend]] - ASP.NET Core development
- [[database]] - Entity Framework Core
- [[testing]] - xUnit, NUnit
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.EntityFrameworkCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=myapp;Username=postgres;Password=postgres",
"Redis": "localhost:6379"
},
"Jwt": {
"Secret": "YOUR_SECRET_KEY_MINIMUM_32_CHARACTERS_LONG",
"Issuer": "MyApp",
"Audience": "MyApp",
"ExpirationInMinutes": 60,
"RefreshExpirationInDays": 7
},
"Cors": {
"AllowedOrigins": [
"http://localhost:3000",
"http://localhost:5173"
]
},
"RateLimiting": {
"PermitLimit": 100,
"WindowInSeconds": 60
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "logs/log-.txt",
"rollingInterval": "Day",
"retainedFileCountLimit": 7
}
}
],
"Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"]
},
"HealthChecks": {
"Enabled": true,
"Path": "/health"
},
"Swagger": {
"Enabled": true,
"Title": "My API",
"Version": "v1"
}
}
C#/.NET Templates
Project configuration templates for .NET applications.
Files
| Template | Purpose |
|---|---|
WebApi.csproj | ASP.NET Core Web API project |
appsettings.json | Application configuration |
Usage
Create new project
# Create solution
dotnet new sln -n MyApp
# Create Web API project
dotnet new webapi -n MyApp.Api -o src/MyApp.Api
# Copy templates
cp templates/WebApi.csproj src/MyApp.Api/MyApp.Api.csproj
cp templates/appsettings.json src/MyApp.Api/appsettings.json
# Add to solution
dotnet sln add src/MyApp.Api/MyApp.Api.csproj
# Restore and build
dotnet restore
dotnet buildCommon Commands
dotnet run # Run application
dotnet watch run # Run with hot reload
dotnet test # Run tests
dotnet ef migrations add Init # Add EF migration
dotnet ef database update # Apply migrations
dotnet publish -c Release # Publish for productionProject Template Features
| Feature | Package |
|---|---|
| ORM | Entity Framework Core |
| Database | PostgreSQL (Npgsql) |
| Auth | JWT Bearer |
| Docs | Swagger/OpenAPI |
| Validation | FluentValidation |
| Mapping | AutoMapper |
| Logging | Serilog |
| Caching | Redis |
| Health | AspNetCore.HealthChecks |
Configuration Sections
appsettings.json
| Section | Purpose |
|---|---|
ConnectionStrings | Database and cache connections |
Jwt | JWT authentication settings |
Cors | Allowed origins |
RateLimiting | Request rate limits |
Serilog | Structured logging |
HealthChecks | Health endpoint config |
Project Structure
MyApp/
├── MyApp.sln
├── src/
│ └── MyApp.Api/
│ ├── MyApp.Api.csproj
│ ├── Program.cs
│ ├── appsettings.json
│ ├── appsettings.Development.json
│ ├── Controllers/
│ ├── Models/
│ ├── Services/
│ └── Data/
│ └── AppDbContext.cs
└── tests/
└── MyApp.Tests/Environment Variables
Override settings via environment:
# Connection string
ConnectionStrings__DefaultConnection="Host=prod-db;..."
# JWT secret
Jwt__Secret="production-secret-key"
# Logging level
Logging__LogLevel__Default="Warning"Docker
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyApp.Api.csproj", "./"]
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApp.Api.dll"]<!--
.NET Web API Project Template
Usage: Copy and rename to {ProjectName}.csproj
-->
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>MyApp</RootNamespace>
<AssemblyName>MyApp</AssemblyName>
<Version>1.0.0</Version>
<Authors>Your Name</Authors>
<Description>Web API application</Description>
</PropertyGroup>
<!-- Code Analysis -->
<PropertyGroup>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisLevel>latest</AnalysisLevel>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<!-- Package References -->
<ItemGroup>
<!-- Entity Framework Core -->
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
<!-- Authentication -->
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
<!-- OpenAPI -->
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<!-- Validation -->
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<!-- Mapping -->
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<!-- Logging -->
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<!-- Health Checks -->
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="8.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="8.0.0" />
<!-- Caching -->
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.0" />
<!-- Code Analysis -->
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
<!-- Test Project References (if needed) -->
<!--
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.0" />
</ItemGroup>
-->
</Project>
Related skills
AI & Agent Buildingagents