C# Cheatsheet
Web, Data, and Testing
Use this C# reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Minimal API
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/health", () => Results.Ok(new { ok = true })); app.MapGet("/users/{id:int}", (int id) => Results.Ok(new { id })); app.MapPost("/users", (CreateUserRequest req) => Results.Created($"/users/{req.Email}", req)); app.Run(); public record CreateUserRequest(string Email, string Name);
ASP.NET Core Minimal APIs bind route values, query strings, and JSON bodies by parameter type. Controllers are still common for larger MVC-style apps.
Dependency Injection
builder.Services.AddSingleton<IClock, SystemClock>(); // one instance forever builder.Services.AddScoped<IUserRepository, PgUserRepo>(); // one per request builder.Services.AddTransient<IEmailSender, SmtpSender>(); // new every resolve app.MapGet("/users/{id:int}", async (int id, IUserRepository users) => await users.FindAsync(id) is { } user ? Results.Ok(user) : Results.NotFound());
The built-in container creates services and injects them into endpoints, controllers, hosted services, and constructors. Scoped services must not be captured by singletons.
Configuration and Logging
string? cs = builder.Configuration.GetConnectionString("Default"); int limit = builder.Configuration.GetValue<int>("RateLimit:PerMinute"); app.MapGet("/work", (ILogger<Program> logger) => { logger.LogInformation("Handling work for {UserId}", 42); // structured logging return Results.Ok(); });
Configuration merges appsettings files, environment variables, user secrets, and command-line values. Never hardcode secrets. Prefer message templates ({UserId}) over string interpolation in log calls.
HttpClient
builder.Services.AddHttpClient("api", client => { client.BaseAddress = new Uri("https://api.example.com/"); client.Timeout = TimeSpan.FromSeconds(5); }); // In a service (using System.Net.Http.Json) var http = httpClientFactory.CreateClient("api"); User? user = await http.GetFromJsonAsync<User>("users/1"); using var res = await http.PostAsJsonAsync("users", new { email = "a@b.co" }); res.EnsureSuccessStatusCode(); string body = await res.Content.ReadAsStringAsync();
Use IHttpClientFactory instead of creating many raw new HttpClient() instances in long-running apps (socket exhaustion).
System.Text.Json
using System.Text.Json; using System.Text.Json.Serialization; public record User( [property: JsonPropertyName("user_name")] string Name, int Age); var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }; string json = JsonSerializer.Serialize(new User("Ada", 36), options); User? back = JsonSerializer.Deserialize<User>(json, options);
// Reading unknown-shape JSON without a model using System.Text.Json.Nodes; JsonNode? node = JsonNode.Parse("""{"user_name":"Ada","tags":["math"]}"""); string? name = (string?)node?["user_name"]; string? firstTag = (string?)node?["tags"]?[0];
System.Text.Json is the built-in serializer used by Minimal APIs and HttpClient JSON extensions. Deserialization is case-insensitive on the web defaults, case-sensitive with plain JsonSerializer unless PropertyNameCaseInsensitive = true.
Entity Framework Core
public class AppDb : DbContext { public AppDb(DbContextOptions<AppDb> options) : base(options) { } public DbSet<User> Users => Set<User>(); } builder.Services.AddDbContext<AppDb>(o => o.UseNpgsql(cs)); // Query var user = await db.Users.SingleOrDefaultAsync(u => u.Email == email); var page = await db.Users .Where(u => u.Active) .OrderBy(u => u.Name) .Skip(20).Take(10) .AsNoTracking() // read-only, faster .ToListAsync(); // Insert, update, delete: one SaveChanges commits the unit of work db.Users.Add(new User { Email = email }); user!.Name = "Ada"; // tracked entity, no explicit Update call needed db.Users.Remove(user); await db.SaveChangesAsync();
dotnet tool install --global dotnet-ef dotnet ef migrations add InitialCreate dotnet ef database update
EF Core maps C# models to database tables and translates LINQ to SQL. Keep queries explicit, understand lazy/eager loading (Include), and inspect generated SQL for hot paths.
xUnit Test Shape
public class CalculatorTests { [Fact] public void Add_ReturnsSum() { Assert.Equal(5, Calculator.Add(2, 3)); } [Theory] [InlineData(2, 3, 5)] [InlineData(-1, 1, 0)] [InlineData(0, 0, 0)] public void Add_HandlesManyCases(int a, int b, int expected) { Assert.Equal(expected, Calculator.Add(a, b)); } [Fact] public void Divide_ByZero_Throws() { var ex = Assert.Throws<ArgumentException>(() => Calculator.Divide(1, 0)); Assert.Contains("zero", ex.Message); } }
[Fact] is a single case; [Theory] + [InlineData] runs one test body over many inputs. Use unit tests for pure logic and integration tests for database, HTTP, serialization, and dependency wiring.
Common Test Commands
dotnet test
dotnet test --filter FullyQualifiedName~Calculator
dotnet test /p:CollectCoverage=trueKeep tests deterministic. Avoid real network calls in unit tests; mock interfaces or use local test servers.
Publish and Runtime
dotnet publish -c Release -o out ASPNETCORE_URLS=http://0.0.0.0:8080 dotnet out/App.dll
Build release artifacts for deployment. Production apps should set environment, logging, health checks, and graceful shutdown behavior.