Lessons in this course (10)
3. From a button click to SQL Server and back
Follow one request — "Load page 1 of orders for customer 42":
1. UI (Angular) — the component asks a service; the service calls the API:
// order.service.ts
@Injectable({ providedIn: 'root' })
export class OrderService {
private http = inject(HttpClient);
getOrders(customerId: number, page = 1, size = 20) {
return this.http.get<PagedResult<OrderDto>>(`/api/customers/${customerId}/orders`, { params: { page, size } });
}
}2. Browser → server — HTTPS request; an interceptor adds Authorization: Bearer <token>; for another origin the browser first sends a CORS preflight (OPTIONS).
3. ASP.NET Core pipeline — Kestrel receives it → middleware in order: exception handler → HTTPS redirection → routing → CORS → authentication → authorization → endpoint.
4. Controller / endpoint — model binding, validation, call the service:
[ApiController]
[Route("api/customers/{customerId:int}/orders")]
public class OrdersController(IOrderService orders) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<PagedResult<OrderDto>>> Get(int customerId, [FromQuery] int page = 1, [FromQuery] int size = 20, CancellationToken ct = default)
=> Ok(await orders.GetPageAsync(customerId, page, Math.Clamp(size, 1, 100), ct));
}5. Service + EF Core — LINQ is translated to one SQL query; AsNoTracking for read-only; project to a DTO so only needed columns travel:
public async Task<PagedResult<OrderDto>> GetPageAsync(int customerId, int page, int size, CancellationToken ct)
{
var query = db.Orders.AsNoTracking().Where(o => o.CustomerId == customerId);
var total = await query.CountAsync(ct);
var items = await query.OrderByDescending(o => o.OrderDate)
.Skip((page - 1) * size).Take(size)
.Select(o => new OrderDto(o.Id, o.OrderDate, o.Status, o.Lines.Sum(l => l.Qty * l.Price)))
.ToListAsync(ct);
return new PagedResult<OrderDto>(items, total, page, size);
}6. SQL Server — runs the generated query (ORDER BY ... OFFSET ... FETCH), ideally an index seek on (CustomerId, OrderDate DESC).
7. Back up the stack — DTOs serialised to JSON by System.Text.Json → 200 OK → Angular deserialises → the template renders with @for.
Interview tip: Mention async all the way (frees threads while waiting for SQL), CancellationToken (stops work when the user navigates away), AsNoTracking and projection. These four show production experience.