Anti-Pattern: The Loop of Death (Report Logic in Loop)
Description
The "Loop of Death" is a performance anti-pattern where a developer iterates over a collection of entities and, for each entity, triggers a synchronous database query (or worse, a write operation). This is the extreme version of the N+1 Query Problem.
In WepNG_BO, this typically happens when generating reports or "Comparing" data structures by walking down a deep object graph without Eager Loading.
Evidence
- Location:
CenterController.CompareCenterYear - Pattern: Nested
foreachloops (Depth 4):foreach (Product product in center.Products) {
foreach (ProductByCatalogue cat in product.ProductByCatalogues) {
foreach (FeeByProductByCatalogue fee in cat.FeeByProductByCatalogues) {
// Accessing Navigation Properties here triggers lazy loading
}
}
} - Cost: If a Center has 10 Products, each with 2 Catalogues, each with 10 Fees:
- 1 Query for Center.
- 1 Query for Products.
- 10 Queries for Catalogues.
- 20 Queries for Fees.
- Total: 1 + 1 + 10 + 20 = 32 Queries (Best Case). Realistically hundreds.
Impact
- Database Spam: The DB receives thousands of tiny queries ("Death by a thousand cuts").
- Thread Starvation: Since this is synchronous, the HTTP Request thread is blocked for the entire duration (which can be seconds or minutes), reducing server capacity.
- Timeout: It is highly likely to time out on larger datasets (e.g., USA destinations).
Remediation
- Abstraction-Safe Prefetching: Use Performance Stabilization Pillar 2: Prefetching to batch load the graph before the loop.
- Stored Procedures: Push the aggregation logic to SQL Server.
- Dapper: Write optimized SQL queries.