C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
133: Building a LINQ-Powered Report Generator
We've all been there: a manager comes to you on a Friday afternoon asking for a "quick" report. They want to see total revenue broken down by region, but only for high-value customers who bought more than three items in the last quarter. When I first started out, my instinct was to reach for the tools I knew best—the foreach loop and a handful of temporary dictionaries. It works, but it's a nightmare to maintain.
The Clutter of Manual Aggregation
If we take the naive approach, we end up writing a lot of "bookkeeping" code. We have to instantiate a dictionary to hold our totals, check if a key exists before adding to it, and manage multiple nested loops to filter the data. Look at this snippet; it's a classic example of imperative reporting:
var regionalTotals = new Dictionary<string, decimal>();
foreach (var order in allOrders)
{
if (order.Customer.Tier == CustomerTier.Enterprise && order.Date >= lastQuarterStart)
{
if (!regionalTotals.ContainsKey(order.Region))
{
regionalTotals[order.Region] = 0;
}
regionalTotals[order.Region] += order.TotalAmount;
}
}
The problem here isn't that the code is "wrong"—it's logically sound. The problem is that the intent is buried under the mechanics. If you come back to this in six months, or if a teammate has to modify the filter to include a specific product category, they have to parse through the dictionary logic just to find the business rule. You're spending more time managing the state of your collection than you are actually defining what the report should contain.
Shifting to a Declarative Pipeline
This is where LINQ transforms the task. Instead of telling the computer how to loop and store data, you tell it what you want. I prefer to think of this as building a pipeline. The data flows in one end, gets filtered, grouped, and shaped, and pops out as a result at the other end.
Here is how I'd write that same report using a LINQ chain:
var report = allOrders
.Where(o => o.Customer.Tier == CustomerTier.Enterprise && o.Date >= lastQuarterStart)
.GroupBy(o => o.Region)
.Select(group => new RegionSummary
{
Region = group.Key,
TotalRevenue = group.Sum(o => o.TotalAmount)
})
.ToList();
Notice the difference in "cognitive load." The Where clause handles the filter, the GroupBy handles the categorization, and the Select handles the projection into a clean report object. There are no temporary dictionaries to manage and no if(!ContainsKey) checks. It reads almost like the English request the manager gave you.
The Cost of Abstraction
Now, I'll be honest with you: there is a trade-off. LINQ creates delegate objects and iterators under the hood, which adds a tiny bit of overhead. In 99% of business applications, this is completely irrelevant. However, if you're writing a high-frequency trading engine or a physics simulator where every nanosecond counts, that overhead might matter. But for a report generator? The trade-off is heavily in favor of LINQ. The "cost" of a few extra CPU cycles is nothing compared to the cost of a developer spending three hours debugging a nested loop in a 500-line method.
One thing to keep in mind is deferred execution. That .ToList() at the end isn't just for convenience; it's what actually tells C# to stop defining the pipeline and start executing it. If you forget it, you're just passing around a "recipe" for a report rather than the report itself. I've seen plenty of juniors wonder why their report is empty or why it's running the same query five times—usually, it's because they're iterating over an IEnumerable without realizing the logic is re-executing on every call.
📋 Practical Task
The Quarterly Regional Performance Auditor
You have been provided with a list of Transaction objects. Each transaction contains a Region (string), an Amount (decimal), a Category (string), and a Timestamp (DateTime).
Your task is to build a report generator that produces a list of CategoryReport objects. The report must follow these requirements:
- Filter out any transactions that occurred before the start of the current year.
- Only include transactions from the "North America" region.
- Group the remaining transactions by their
Category. - For each category, calculate the total sum of
Amountand the averageAmount.
Create a method GenerateAuditReport(List<Transaction> transactions) that returns a List<CategoryReport> using a single LINQ chain. Avoid using any foreach loops or manual dictionary management.
There are no comments for now.