ASP.NET
Updated: May 22, 2026Categories: Languages, Framework, Web, Frontend
Printed from:
ASP.NET Cheatsheet
1. Installation and Setup
.NET SDK Installation
Bash
12345678910111213141516# Download .NET SDK from https://dot.net (current LTS: .NET 10)
# Or use a package manager
# macOS (Homebrew)
brew install --cask dotnet-sdk
# Windows (winget)
winget install Microsoft.DotNet.SDK.10
# Linux (script install)
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel LTS
# Verify installation
dotnet --version
dotnet --list-sdks
IDE Setup
- Visual Studio 2022+: Recommended on Windows
- Visual Studio Code: Cross-platform with the official C# Dev Kit extension
- JetBrains Rider: Cross-platform alternative (free for non-commercial use)
Project Creation
Bash
123456789101112131415161718# Create new ASP.NET Core Web App (Razor Pages)
dotnet new webapp -n MyWebApp
# Create Web API project (minimal APIs by default)
dotnet new webapi -n MyApiProject
# Use controller-based Web API
dotnet new webapi -n MyApiProject --use-controllers
# Create MVC project
dotnet new mvc -n MyMvcProject
# Create Blazor Web App (unified Server + WebAssembly)
dotnet new blazor -n MyBlazorApp
# List all available templates
dotnet new list
2. Project Structure
MyWebApp/
│
├── Controllers/ # Request handling logic (MVC / Web API)
│ └── HomeController.cs
│
├── Models/ # Data models / DTOs
│ └── User.cs
│
├── Views/ # Razor view templates
│ ├── Home/
│ │ └── Index.cshtml
│ └── Shared/
│ └── _Layout.cshtml
│
├── wwwroot/ # Static web assets
│ ├── css/
│ ├── js/
│ └── images/
│
├── Properties/
│ └── launchSettings.json
│
├── appsettings.json # Base configuration
├── appsettings.Development.json # Environment overrides
└── Program.cs # Application entry point (top-level statements)
3. Controllers
Basic Controller
C#
123456789101112131415161718192021222324public class HomeController : Controller
{
private readonly IRepository _repository;
public HomeController(IRepository repository) => _repository = repository;
// Simple action method
public IActionResult Index() => View();
// Async action with parameter
public async Task<IActionResult> Details(int id)
{
var item = await _repository.GetByIdAsync(id);
if (item is null) return NotFound();
return View(item);
}
// Different action results
public IActionResult Download()
=> PhysicalFile("/path/to/file", "application/pdf");
public IActionResult RedirectToHome() => RedirectToAction(nameof(Index));
}
Routing
C#
1234567891011// Attribute Routing
[Route("[controller]/[action]")]
public class UsersController : Controller
{
[HttpGet("{id:int}")]
public IActionResult GetUser(int id) => Ok();
[HttpPost]
public IActionResult CreateUser([FromBody] UserModel model) => Ok(model);
}
4. Views
Razor Syntax
cshtml
123456789101112131415161718@{ // Server-side code block var name = "John"; var currentTime = DateTime.Now; } <h1>Hello, @name!</h1> @if (currentTime.Hour < 12) { <p>Good morning!</p> } @foreach (var item in Model.Items) { <div>@item.Name</div> }
Layout and Partial Views
cshtml
123456789101112131415161718<!-- _Layout.cshtml --> <!DOCTYPE html> <html> <head> @RenderSection("head", required: false) </head> <body> @RenderBody() <partial name="_Footer" /> </body> </html> <!-- Partial View: _UserCard.cshtml --> <div class="user-card"> <h3>@Model.Name</h3> <p>@Model.Email</p> </div>
Tag Helpers
cshtml
12345678<form asp-controller="Users" asp-action="Create" method="post"> <input asp-for="Name" class="form-control" /> <span asp-validation-for="Name" class="text-danger"></span> <button type="submit">Save</button> </form> <a asp-controller="Users" asp-action="Details" asp-route-id="@user.Id">View</a>
5. Models and Data
Model Definition
C#
123456789101112131415public class User
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; } = string.Empty;
[EmailAddress]
public string Email { get; set; } = string.Empty;
[Range(18, 100)]
public int Age { get; set; }
}
Entity Framework Core
C#
1234567891011public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options) { }
public DbSet<User> Users => Set<User>();
}
// Register in Program.cs
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
6. Routing
The Modern Program.cs (top-level statements)
ASP.NET Core no longer uses Startup.cs by default. Services and the request
pipeline are configured directly on WebApplicationBuilder / WebApplication.
C#
123456789101112131415161718192021222324var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles(); // or app.MapStaticAssets() in .NET 9+
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Attribute Routing
C#
12345678910[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet("{id:int}")]
public IActionResult GetProduct(int id) => Ok();
[HttpGet("category/{category}")]
public IActionResult GetByCategory(string category) => Ok();
}
Minimal APIs
C#
123456789101112131415161718var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/products/{id:int}", (int id, IProductService svc) => svc.Get(id));
app.MapPost("/products", async (Product p, IProductService svc) =>
{
var created = await svc.CreateAsync(p);
return Results.Created($"/products/{created.Id}", created);
});
// Group related endpoints
var users = app.MapGroup("/users").RequireAuthorization();
users.MapGet("/", () => "All users");
users.MapGet("/{id:int}", (int id) => $"User {id}");
app.Run();
7. Middleware
Custom Middleware
C#
123456789101112131415161718192021222324252627282930313233343536public class RequestLoggerMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestLoggerMiddleware> _logger;
public RequestLoggerMiddleware(RequestDelegate next, ILogger<RequestLoggerMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
_logger.LogInformation("Request: {Method} {Path}", context.Request.Method, context.Request.Path);
await _next(context);
}
}
// Extension method for easy middleware registration
public static class MiddlewareExtensions
{
public static IApplicationBuilder UseRequestLogger(this IApplicationBuilder builder)
=> builder.UseMiddleware<RequestLoggerMiddleware>();
}
// In Program.cs
app.UseRequestLogger();
// Or inline using Use/Map
app.Use(async (context, next) =>
{
// before
await next();
// after
});
8. Dependency Injection
Service Registration
C#
123456789101112131415// Transient: new instance every time it's requested
builder.Services.AddTransient<IUserService, UserService>();
// Scoped: one instance per HTTP request
builder.Services.AddScoped<IOrderService, OrderService>();
// Singleton: one instance for the entire application lifetime
builder.Services.AddSingleton<IConfigService, ConfigService>();
// Keyed services (.NET 8+)
builder.Services.AddKeyedScoped<INotifier, EmailNotifier>("email");
builder.Services.AddKeyedScoped<INotifier, SmsNotifier>("sms");
public class AlertService([FromKeyedServices("email")] INotifier notifier) { /* ... */ }
9. Configuration
appsettings.json
JSON
12345678910111213{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MyApp;Trusted_Connection=True;TrustServerCertificate=True"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Configuration in Code
C#
1234567891011121314151617// Bind a section to a strongly typed POCO
builder.Services.Configure<AppSettings>(builder.Configuration.GetSection("AppSettings"));
// Direct access
var connStr = builder.Configuration.GetConnectionString("DefaultConnection");
var value = builder.Configuration["AppSettings:SomeKey"];
// Inject IOptions<T> / IOptionsSnapshot<T> / IOptionsMonitor<T>
public class MyService
{
public MyService(IOptions<AppSettings> options) { /* ... */ }
}
// User Secrets (development only)
// dotnet user-secrets init
// dotnet user-secrets set "ConnectionStrings:DefaultConnection" "..."
10. Authentication & Authorization
Identity + JWT Setup
C#
1234567891011121314151617181920212223242526272829builder.Services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!))
};
});
// Policy-based authorization
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
});
// Pipeline order matters
app.UseAuthentication();
app.UseAuthorization();
11. Web API
Controller-based API
C#
123456789101112131415161718192021222324252627[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
public UsersController(IUserService userService) => _userService = userService;
[HttpGet]
public async Task<ActionResult<IEnumerable<UserDto>>> GetUsers()
=> Ok(await _userService.GetAllAsync());
[HttpGet("{id:int}")]
public async Task<ActionResult<UserDto>> GetUser(int id)
{
var user = await _userService.GetAsync(id);
return user is null ? NotFound() : Ok(user);
}
[HttpPost]
public async Task<IActionResult> CreateUser([FromBody] UserDto user)
{
var result = await _userService.CreateAsync(user);
return CreatedAtAction(nameof(GetUser), new { id = result.Id }, result);
}
}
OpenAPI / Swagger
C#
123456789// .NET 9+ ships built-in OpenAPI document generation
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi(); // exposes /openapi/v1.json
// For a Swagger UI, add the Swashbuckle.AspNetCore NuGet package and:
// app.UseSwaggerUI(opt => opt.SwaggerEndpoint("/openapi/v1.json", "v1"));
12. Database Operations
EF Core Migrations
Bash
123456789101112131415# Install the EF Core CLI (one-time, global)
dotnet tool install --global dotnet-ef
# Create migration
dotnet ef migrations add InitialCreate
# Update database
dotnet ef database update
# Roll back to a previous migration
dotnet ef database update LastGoodMigration
# Generate an idempotent SQL script for deployment
dotnet ef migrations script --idempotent -o migrate.sql
LINQ Queries
C#
12345678910111213141516171819// Simple async query
var activeUsers = await _context.Users
.Where(u => u.IsActive)
.OrderBy(u => u.Name)
.AsNoTracking()
.ToListAsync();
// Complex query with includes
var usersWithPosts = await _context.Users
.Include(u => u.Posts)
.ThenInclude(p => p.Comments)
.Where(u => u.Posts.Any())
.ToListAsync();
// Bulk operations (EF Core 7+)
await _context.Users.Where(u => !u.IsActive).ExecuteDeleteAsync();
await _context.Users.Where(u => u.IsActive)
.ExecuteUpdateAsync(s => s.SetProperty(u => u.LastSeen, DateTime.UtcNow));
13. Logging and Error Handling
Logging Configuration
C#
1234567891011121314151617181920212223242526// Console + Debug providers are added by default via CreateBuilder.
// Add more providers as needed:
builder.Logging.AddJsonConsole();
// In a controller or service
public class UserService
{
private readonly ILogger<UserService> _logger;
public UserService(ILogger<UserService> logger) => _logger = logger;
public void ProcessUser(User user)
{
try
{
_logger.LogInformation("Processing user {UserId}", user.Id);
// Processing logic
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing user {UserId}", user.Id);
throw;
}
}
}
Global Exception Handling
C#
12345678910111213141516171819202122// Implement IExceptionHandler (recommended in .NET 8+)
public class GlobalExceptionHandler : IExceptionHandler
{
public async ValueTask<bool> TryHandleAsync(
HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
{
var problem = new ProblemDetails
{
Status = StatusCodes.Status500InternalServerError,
Title = "Server error",
Detail = exception.Message
};
httpContext.Response.StatusCode = problem.Status.Value;
await httpContext.Response.WriteAsJsonAsync(problem, cancellationToken);
return true;
}
}
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
builder.Services.AddProblemDetails();
app.UseExceptionHandler();
14. Testing
Unit Testing
C#
123456789101112131415161718192021public class UserControllerTests
{
[Fact]
public async Task GetUser_ReturnsUser_WhenUserExists()
{
// Arrange
var mockService = new Mock<IUserService>();
mockService.Setup(x => x.GetAsync(1))
.ReturnsAsync(new UserDto { Id = 1 });
var controller = new UsersController(mockService.Object);
// Act
var result = await controller.GetUser(1);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result.Result);
var user = Assert.IsType<UserDto>(okResult.Value);
Assert.Equal(1, user.Id);
}
}
Integration Testing
C#
12345678910111213141516// Reference Microsoft.AspNetCore.Mvc.Testing
public class ApiTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public ApiTests(WebApplicationFactory<Program> factory)
=> _client = factory.CreateClient();
[Fact]
public async Task GetUsers_ReturnsSuccess()
{
var response = await _client.GetAsync("/api/users");
response.EnsureSuccessStatusCode();
}
}
15. Deployment and Publishing
Bash
123456789101112131415161718# Framework-dependent publish
dotnet publish -c Release -o ./publish
# Self-contained, single-file deployment for Linux
dotnet publish -c Release -r linux-x64 --self-contained true \
-p:PublishSingleFile=true
# Trimmed deployment (smaller binary)
dotnet publish -c Release -r linux-x64 --self-contained true \
-p:PublishTrimmed=true
# Native AOT (faster startup, smaller, no JIT) — supported for ASP.NET Core
# Web APIs / minimal APIs
dotnet publish -c Release -r linux-x64 -p:PublishAot=true
# Container image (no Dockerfile required)
dotnet publish -c Release -t:PublishContainer
16. Common Commands
Bash
123456789101112131415161718192021222324252627# Create / scaffold
dotnet new webapp
dotnet new sln && dotnet sln add MyApp.csproj
# Restore packages (usually implicit)
dotnet restore
# Run project (watch mode for hot reload)
dotnet run
dotnet watch
# Build / test
dotnet build
dotnet test
# NuGet packages
dotnet add package PackageName
dotnet list package --outdated
dotnet remove package PackageName
# Manage .NET tools
dotnet tool install --global dotnet-ef
dotnet tool update --global dotnet-ef
# Format code (built-in)
dotnet format
17. Best Practices and Patterns
- Use built-in Dependency Injection — avoid service locators.
- Keep controllers thin; push logic into services and domain types.
- Prefer
async/awaitfor I/O-bound operations and propagateCancellationTokens. - Return
ProblemDetails(RFC 9457) for error responses in Web APIs. - Use environment-specific configuration (
appsettings.{Environment}.json, user secrets, Key Vault). - Enforce HTTPS, HSTS, and authentication; validate all input.
- Use
AsNoTracking()for read-only EF Core queries; project to DTOs for APIs. - Add structured logging with message templates (not interpolated strings).
- Write unit tests and use
WebApplicationFactory<TEntryPoint>for integration tests. - Stay on a supported runtime — currently .NET 10 (LTS) and .NET 9 (STS, end of support May 12 2026). .NET 8 LTS reaches end of support November 10 2026; .NET 6 and .NET 7 are out of support.
- Consider Native AOT for small, fast-starting APIs and serverless workloads.
- Use
MapStaticAssets()(.NET 9+) for compressed, fingerprinted static files.
Note: This cheatsheet covers core concepts. Always refer to the latest Microsoft documentation at https://learn.microsoft.com/aspnet/core for the most up-to-date information.
Continue Learning
Discover more cheatsheets to boost your productivity