Loading...
EC

Erick Castillo

Lead .Net/Azure Developer

View Portfolio

Hexaware Technologies

Lead .NET Developer

October 2025 - February 2026

The Problem

The ELA platform's REST API had endpoints that performed acceptably under normal traffic but degraded under the spikes the platform sees during active disaster-response windows. Common issues: N+1 query patterns in EF Core, list endpoints returning full entities with every related collection eagerly loaded, async methods that quietly ran synchronously because of a single blocking call, and pagination implemented with Skip/Take against large tables where offset-based paging gets progressively slower. On a federal platform where a traffic spike correlates with the worst possible moment to slow down, the API needed to be tightened before the next event, not after.

The Solution

Profiled and hardened ASP.NET Core REST endpoints across the ELA platform, fixing N+1 query patterns, replacing offset pagination with cursor-based pagination on hot list endpoints, enforcing async-all-the-way-down, and adding response compression and output caching where it was safe. Used MiniProfiler and EF Core query logging to surface the worst offenders, fixed them at the handler and EF Core configuration level rather than papering over with caching, then added caching on top of an already-fast baseline.

Implementation

Turned on EF Core sensitive-data logging in dev and ran realistic workloads against MiniProfiler to surface N+1 patterns — endpoints that called .Include() for one relationship and then hit .LazyLoading for another, or projected to DTOs after materializing full entities instead of projecting in the query. Rewrote the offenders to project directly with .Select(e => new Dto { ... }) so EF Core generated a single SQL statement instead of a parent query plus per-row child queries. Replaced offset pagination on hot list endpoints with cursor-based pagination using a composite key ((SortColumn, Id)) so page-N performance stayed constant instead of degrading linearly. Audited async paths for .Result / .Wait() calls and Task.Run misuse, fixed them so the request thread actually yielded under load, and used IAsyncEnumerable<T> for streaming endpoints where the client wanted progressive results. Enabled response compression (Brotli + Gzip), turned on output caching for genuinely cacheable GET endpoints with appropriate vary-by keys, and added ETag support so clients could short-circuit on unchanged data. Added covering indexes in MySQL for the queries that showed up hottest in production traces, and used raw SQL via EF Core for two query paths where the LINQ translation generated SQL that the optimizer couldn't handle well. Wrapped the API in structured logging with correlation IDs through MediatR pipeline behaviors so every request was traceable end-to-end, and wired the logs into Azure Monitor for production observability.

Technologies

C# ASP.NET Core .NET 9 Entity Framework Core Pomelo.EntityFrameworkCore.MySql MiniProfiler MySQL 8.0 Brotli/Gzip response compression output caching ETag async/await IAsyncEnumerable<T> cursor-based pagination Azure Monitor / Application Insights structured logging

Impact & Results

Hot endpoints stopped degrading under load because the fixes happened at the query and async layers rather than being hidden behind caching. Cursor-based pagination meant deep pages performed as well as shallow ones, which mattered for admin and reporting endpoints that legitimately needed to scroll far into result sets. Async-all-the-way-down freed up request threads under spike traffic, which raised the platform's effective throughput on the same infrastructure. Response compression and output caching layered on top gave additional headroom without masking underlying problems. Structured logging plus Azure Monitor meant the next regression would be easy to find — observability became a real capability, not a checkbox.

N+1 query patterns eliminated on identified hot endpoints

Offset pagination replaced with cursor-based pagination on hot list endpoints

Async-all-the-way-down enforced; blocking .Result / .Wait() calls removed

Brotli + Gzip response compression enabled platform-wide

Output caching + ETag support added where safe

Covering indexes added in MySQL for the hottest production queries

Structured logging with correlation IDs wired into Azure Monitor

This entry is public
0 Likes
0 Vouches

Vouches (0)

Vouches verify the authenticity of this work. Each voucher explains their connection to the work.

Loading vouches...