Summary
ASP.NET Core is Microsoft's cross-platform, high-performance web framework for building modern web applications and APIs. This cheatsheet covers essential concepts including fundamentals, dependency injection, middleware, routing, authentication, Entity Framework Core, and performance optimization. Key topics include the request pipeline, service lifetimes, model binding, filters, and best practices for building scalable web applications.
1. ASP.NET Core Fundamentals
What is ASP.NET Core?
- Cross-platform, high-performance, open-source framework
- Built on .NET Core (now .NET 5+)
- Supports Windows, Linux, macOS
- Unified framework for web UI and APIs
Key Features
- Cross-platform: Run on Windows, Linux, macOS
- High Performance: One of the fastest web frameworks
- Dependency Injection: Built-in DI container
- Modular: NuGet-based, use only what you need
- Cloud-ready: Configuration, logging, DI designed for cloud
Hosting Models
- Kestrel: Cross-platform web server (recommended)
- IIS: Windows-only, acts as reverse proxy to Kestrel
- HTTP.sys: Windows-only, alternative to Kestrel
2. Project Structure
Program.cs (Minimal API .NET 6+)
var builder = WebApplication.CreateBuilder(args);
// Add services
builder.Services.AddControllers();
builder.Services.AddDbContext<AppDbContext>();
var app = builder.Build();
// Configure middleware pipeline
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Key Folders
- Controllers: API/MVC controllers
- Models: Data models/DTOs
- Views: Razor views (MVC)
- wwwroot: Static files (CSS, JS, images)
- appsettings.json: Configuration
3. Dependency Injection (DI)
Service Lifetimes
// Transient: New instance each time
builder.Services.AddTransient<IEmailService, EmailService>();
// Scoped: One instance per request
builder.Services.AddScoped<IUserService, UserService>();
// Singleton: One instance for app lifetime
builder.Services.AddSingleton<ICacheService, CacheService>();
Constructor Injection
public class UserController : ControllerBase
{
private readonly IUserService _userService;
public UserController(IUserService userService)
{
_userService = userService;
}
}
4. Middleware
What is Middleware?
- Components that handle HTTP requests/responses
- Executed in order (pipeline)
- Can short-circuit the pipeline
Built-in Middleware Order (Important!)
app.UseExceptionHandler("/Error"); // 1. Exception handling (first)
app.UseHsts(); // 2. HTTPS Strict Transport Security
app.UseHttpsRedirection(); // 3. HTTPS redirection
app.UseStaticFiles(); // 4. Static files (before routing)
app.UseRouting(); // 5. Route matching
app.UseCors(); // 6. CORS (after routing)
app.UseAuthentication(); // 7. Authentication (before authorization)
app.UseAuthorization(); // 8. Authorization (after authentication)
app.MapControllers(); // 9. Map endpoints (.NET 6+)
Custom Middleware
public class LoggingMiddleware
{
private readonly RequestDelegate _next;
public LoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// Before
Console.WriteLine($"Request: {context.Request.Path}");
await _next(context);
// After
Console.WriteLine($"Response: {context.Response.StatusCode}");
}
}
// Register
app.UseMiddleware<LoggingMiddleware>();
5. Routing
Attribute Routing
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult GetAll() { }
[HttpGet("{id:int}")]
public IActionResult GetById(int id) { }
[HttpPost]
public IActionResult Create([FromBody] Product product) { }
[HttpPut("{id}")]
public IActionResult Update(int id, [FromBody] Product product) { }
[HttpDelete("{id}")]
public IActionResult Delete(int id) { }
}
Route Constraints
[HttpGet("{id:int:min(1)}")] // int, minimum value 1
[HttpGet("{name:alpha}")] // alphabetic only
[HttpGet("{price:decimal}")] // decimal values
[HttpGet("{date:datetime}")] // DateTime
[HttpGet("{id:guid}")] // GUID
6. Controllers and Actions
Action Results
// Status Codes
return Ok(data); // 200
return Created(uri, data); // 201
return NoContent(); // 204
return BadRequest(error); // 400
return Unauthorized(); // 401
return NotFound(); // 404
return Conflict(); // 409
// Generic Results
return StatusCode(500, "Error");
return new JsonResult(data);
return File(bytes, "application/pdf");
return Redirect("https://example.com");
Model Binding Sources
public IActionResult Search(
[FromQuery] string q, // From query string
[FromRoute] int id, // From route data
[FromBody] Product product, // From request body
[FromHeader] string auth, // From headers
[FromForm] IFormFile file) // From form data
{
// Implementation
}
7. Model Validation
Data Annotations
public class ProductDto
{
[Required(ErrorMessage = "Name is required")]
[StringLength(100, MinimumLength = 3)]
public string Name { get; set; }
[Range(0.01, 10000)]
public decimal Price { get; set; }
[EmailAddress]
public string Email { get; set; }
[RegularExpression(@"^\d{3}-\d{3}-\d{4}$")]
public string Phone { get; set; }
[Compare("Password")]
public string ConfirmPassword { get; set; }
}
Manual Validation
[HttpPost]
public IActionResult Create(ProductDto product)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
// Custom validation
if (product.Price < 0)
ModelState.AddModelError("Price", "Price cannot be negative");
return Ok();
}
8. Filters
Filter Types (Execution Order)
- Authorization Filters: First to run
- Resource Filters: Before/after model binding
- Action Filters: Before/after action execution
- Exception Filters: Handle exceptions
- Result Filters: Before/after action result
Custom Action Filter
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
// Before action executes
Log($"Executing {context.ActionDescriptor.DisplayName}");
}
public override void OnActionExecuted(ActionExecutedContext context)
{
// After action executes
Log($"Executed {context.ActionDescriptor.DisplayName}");
}
}
// Usage
[LogActionFilter]
public class ProductsController : ControllerBase { }
9. Configuration
appsettings.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=.;Database=MyDb;Trusted_Connection=true;"
},
"Logging": {
"LogLevel": {
"Default": "Information"
}
},
"AppSettings": {
"ApiKey": "your-api-key",
"MaxItems": 100
}
}
Accessing Configuration
// Using IConfiguration
public class MyService
{
private readonly IConfiguration _config;
public MyService(IConfiguration config)
{
_config = config;
var connString = _config.GetConnectionString("DefaultConnection");
var apiKey = _config["AppSettings:ApiKey"];
}
}
// Using Options Pattern
public class AppSettings
{
public string ApiKey { get; set; }
public int MaxItems { get; set; }
}
// Startup
builder.Services.Configure<AppSettings>(
builder.Configuration.GetSection("AppSettings"));
// Usage
public class MyService
{
private readonly AppSettings _settings;
public MyService(IOptions<AppSettings> options)
{
_settings = options.Value;
}
}
10. Logging
Built-in Logging
public class ProductService
{
private readonly ILogger<ProductService> _logger;
public ProductService(ILogger<ProductService> logger)
{
_logger = logger;
}
public void ProcessProduct(int id)
{
_logger.LogInformation("Processing product {ProductId}", id);
try
{
// Process
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing product {ProductId}", id);
}
}
}
Log Levels
- Trace: Most detailed messages
- Debug: Debugging information
- Information: General flow
- Warning: Abnormal or unexpected events
- Error: Error messages
- Critical: Failures requiring immediate attention
- None: Not used for logging
11. Entity Framework Core
DbContext Setup
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options) { }
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Fluent API configuration
modelBuilder.Entity<Product>()
.HasKey(p => p.Id);
modelBuilder.Entity<Product>()
.Property(p => p.Name)
.IsRequired()
.HasMaxLength(100);
}
}
// Registration
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString));
Common Operations
// Query with filtering and ordering
var products = await _context.Products
.Where(p => p.Price > 100)
.OrderBy(p => p.Name)
.ToListAsync();
// Include related data (eager loading)
var productsWithCategory = await _context.Products
.Include(p => p.Category)
.ToListAsync();
// Projection (select specific fields)
var productNames = await _context.Products
.Select(p => p.Name)
.ToListAsync();
// CRUD Operations
// Create
_context.Products.Add(new Product { Name = "New Product" });
await _context.SaveChangesAsync();
// Read
var product = await _context.Products.FindAsync(id);
// Update
product.Name = "Updated Name";
await _context.SaveChangesAsync();
// Delete
_context.Products.Remove(product);
await _context.SaveChangesAsync();
13. Web API Best Practices
API Versioning
// Install: Microsoft.AspNetCore.Mvc.Versioning
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
});
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class ProductsController : ControllerBase { }
Response Caching
// Response caching
[HttpGet]
[ResponseCache(Duration = 60)] // Cache for 60 seconds
public IActionResult GetProducts() { }
// In-memory caching
public class ProductService
{
private readonly IMemoryCache _cache;
public async Task<Product> GetProductAsync(int id)
{
var cacheKey = $"product_{id}";
if (!_cache.TryGetValue(cacheKey, out Product product))
{
product = await _repository.GetByIdAsync(id);
_cache.Set(cacheKey, product, TimeSpan.FromMinutes(5));
}
return product;
}
}
Global Exception Handling
public class GlobalExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<GlobalExceptionMiddleware> _logger;
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "An unhandled exception occurred");
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(
HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
var response = new
{
error = new
{
message = "An error occurred processing your request",
detail = exception.Message // Only in development
}
};
await context.Response.WriteAsync(JsonSerializer.Serialize(response));
}
}
14. Performance Optimization
Async/Await Best Practices
// Good - Async all the way
public async Task<IActionResult> GetProductsAsync()
{
var products = await _service.GetProductsAsync();
return Ok(products);
}
// Avoid - Sync over async
public IActionResult GetProducts()
{
var products = _service.GetProductsAsync().Result; // Blocks thread
return Ok(products);
}
// Parallel execution
public async Task<IActionResult> GetDashboardData()
{
var productsTask = _productService.GetProductsAsync();
var ordersTask = _orderService.GetOrdersAsync();
await Task.WhenAll(productsTask, ordersTask);
return Ok(new
{
Products = productsTask.Result,
Orders = ordersTask.Result
});
}
Response Compression
// Enable response compression
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<GzipCompressionProvider>();
});
app.UseResponseCompression();
16. Quick Reference - Common Attributes
Controller Attributes
[ApiController]: Enables API-specific behaviors (auto model validation, binding source inference)[Route("api/[controller]")]: Defines route template[Authorize]: Requires authentication[AllowAnonymous]: Allows anonymous access
Action Attributes
[HttpGet],[HttpPost],[HttpPut],[HttpDelete]: HTTP verb constraints[FromBody],[FromQuery],[FromRoute],[FromHeader]: Binding sources[ResponseCache(Duration = 60)]: Response caching[ValidateAntiForgeryToken]: CSRF protection
Model Validation Attributes
[Required]: Field is mandatory[StringLength(100, MinimumLength = 3)]: String length constraints[Range(1, 100)]: Numeric range validation[EmailAddress]: Email format validation[RegularExpression("pattern")]: Custom pattern validation[Compare("PropertyName")]: Compare with another property
17. Testing
Unit Testing Controllers
[Fact]
public async Task GetProduct_ReturnsProduct_WhenProductExists()
{
// Arrange
var mockService = new Mock<IProductService>();
mockService.Setup(s => s.GetByIdAsync(1))
.ReturnsAsync(new Product { Id = 1, Name = "Test" });
var controller = new ProductsController(mockService.Object);
// Act
var result = await controller.GetById(1);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var product = Assert.IsType<Product>(okResult.Value);
Assert.Equal(1, product.Id);
}
Integration Testing
public class ProductsControllerTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
public ProductsControllerTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
}
[Fact]
public async Task Get_Products_ReturnsSuccessStatusCode()
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync("/api/products");
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal("application/json",
response.Content.Headers.ContentType.MediaType);
}
}
18. Security Best Practices
CORS Configuration
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigin",
builder => builder
.WithOrigins("https://example.com")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
app.UseCors("AllowSpecificOrigin");
Data Protection
// Protect sensitive data
public class User
{
public int Id { get; set; }
public string Username { get; set; }
[JsonIgnore] // Don't serialize
public string Password { get; set; }
}
// Input validation
[HttpPost]
public IActionResult Create([FromBody] ProductDto product)
{
// Validate input
if (string.IsNullOrWhiteSpace(product.Name))
return BadRequest("Product name is required");
// Sanitize input
product.Name = product.Name.Trim();
// Process...
}
19. Key Concepts & Comparisons
.NET Core vs .NET Framework
| Feature | .NET Core | .NET Framework |
|---|---|---|
| Platform | Cross-platform | Windows only |
| Performance | Higher | Lower |
| Deployment | Self-contained | System-wide |
| Open Source | Yes | Partially |
| Future | Active development | Maintenance mode |
Data Passing Methods (MVC)
- ViewData: Dictionary-based, requires casting, weak typing
- ViewBag: Dynamic wrapper around ViewData
- TempData: Persists for one request, uses session storage
- Model: Strongly typed, compile-time checking (recommended)
Action Result Types
| Result Type | Purpose | Status Code |
|---|---|---|
| ViewResult | Return a view | 200 |
| JsonResult | Return JSON data | 200 |
| ContentResult | Return plain text | 200 |
| FileResult | Return file download | 200 |
| RedirectResult | Redirect to URL | 302 |
| StatusCodeResult | Custom status code | Variable |
Authentication vs Authorization
- Authentication: Identity verification ("Who are you?")
- Authorization: Permission checking ("What can you do?")
- Flow: Authentication → Authorization → Access granted/denied
Performance Optimization Strategies
- Async/Await: Use consistently, avoid blocking calls
- Caching: Response caching, in-memory, distributed (Redis)
- Database: Use projections, includes, avoid N+1 queries
- Compression: Enable Gzip compression for responses
- Pagination: Limit large dataset responses
- CDN: Serve static content from edge locations
Quick Reference - Status Codes
- 1xx: Informational
- 2xx: Success (200 OK, 201 Created, 204 No Content)
- 3xx: Redirection (301 Moved, 302 Found, 304 Not Modified)
- 4xx: Client Error (400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found)
- 5xx: Server Error (500 Internal Server Error, 503 Service Unavailable)
Key Commands
# Create new project
dotnet new webapi -n MyApi
dotnet new mvc -n MyMvcApp
# Add packages
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
# EF Core commands
dotnet ef migrations add InitialCreate
dotnet ef database update
dotnet ef database drop
# Run application
dotnet run
dotnet watch run # With hot reload
Remember: This cheat sheet covers the essentials. Always refer to official documentation for the latest updates and detailed information.