1. Use let to Simplify Complex Projections
With let, you can store that value once and reuse it:
var result = from user in users
let fullName = user.FirstName + " " + user.LastName
where fullName.Contains("John")
select new { user.Id, fullName };
Here, instead of writing user.FirstName + " " + user.LastName multiple times, we save it as fullName. This improves readability and avoids mistakes.
2. Use Aggregate for Custom Reductions
Sometimes you want to do something beyond Sum, Count, or Average. For example, joining strings into a sentence. LINQ provides Aggregate for this.
string sentence = words.Aggregate((acc, w) => acc + " " + w);
Here, Aggregate works like reduce or fold in other languages. It gives you full control over how items are combined.
3. Group and Project with GroupBy and Anonymous Types
When building reports or summaries, grouping is a common need. LINQ allows grouping by one or more fields and then projecting useful results.
var grouped = orders
.GroupBy(o => new { o.CustomerId, o.Status })
.Select(g => new {
g.Key.CustomerId,
g.Key.Status,
TotalAmount = g.Sum(x => x.Amount)
});
This way, you can group orders by customer and status and directly calculate totals in one query. Perfect for dashboards and analytics.
4. Use ToLookup for Fast Lookups by Key
If you often need to group items by a key and then quickly access them, ToLookup is very handy. It creates a dictionary-like structure where one key can point to multiple values.
var lookup = people.ToLookup(p => p.Department);
foreach (var person in lookup["HR"]) {
Console.WriteLine(person.Name);
}
Here, all people in the "HR" department can be fetched in one step. This is faster and cleaner than filtering repeatedly.
5. Apply Conditional Filters with Dynamic Queries
Sometimes, you don’t know if you should apply a filter until runtime (e.g., if the search box is empty). Writing multiple queries can be messy. Instead, you can conditionally apply filters.
if (!string.IsNullOrEmpty(searchTerm))
{
query = query.Where(x => x.Name.Contains(searchTerm));
}
Or even better, you can use an extension method:
public static IQueryable<T> WhereIf<T>(
this IQueryable<T> source,
bool condition,
Expression<Func<T, bool>> predicate)
{
return condition ? source.Where(predicate) : source;
}
This makes your queries dynamic and flexible.
6. Use SelectMany to Flatten Nested Collections
Often, you have nested collections (like articles with tags). Instead of looping through them manually, use SelectMany.
var allTags = articles.SelectMany(a => a.Tags).Distinct();
Here, you get all tags from all articles in one flat list. No nested loops needed.
7. Enable Debugging with AsEnumerable() and Logging
When using Entity Framework (EF Core), your queries are first translated to SQL. Sometimes, you want to debug part of the query in memory instead.
var result = db.Products
.Where(p => p.Price > 0)
.AsEnumerable() // switch from SQL to in-memory
.Where(p => IsSpecial(p));
By calling AsEnumerable(), you stop translating queries into SQL and continue with C# logic. Use this carefully because it pulls data into memory. It’s good for debugging, but avoid overusing it in production for large datasets.
8. Avoid Repeated Enumeration with .ToList() or .ToArray()
LINQ queries are lazy — they don’t run until you actually use them. If you reuse the same query multiple times, it will execute again each time. This can hurt performance.
var result = expensiveQuery.ToList();
DoSomething(result);
DoSomethingElse(result);
By materializing the query with ToList() or ToArray(), you ensure the query runs only once. This avoids duplicate database calls or calculations.
9. Paginate Results Using Skip and Take
When building APIs or web pages, you often need to show data page by page. LINQ makes this easy with Skip and Take.
var page = users
.OrderBy(u => u.Id)
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToList();
This ensures your app only fetches the required records instead of loading everything. Always combine with OrderBy for consistent results.
10. Combine with Expression Trees for Reusability
For complex filters, repeating logic can get messy. Instead, you can use expressions that are reusable across queries.
Expression<Func<User, bool>> isActive =
u => u.IsActive && u.LastLogin > DateTime.UtcNow.AddDays(-30);
var result = db.Users.Where(isActive);
This approach makes your filters reusable, composable, and testable. It’s especially useful for enterprise-level applications.
Conclusion
-
Improve query performance in your .NET applications
-
Build maintainable, enterprise-ready APIs and dashboards
- Scale your development with skilled professionals
Let Sparkle Web be your trusted partner. Contact us today and take your .NET projects to the next level.
Brijesh Hirapara
A highly skilled .NET Full Stack Developer proficient in MVC, .NET Core, C#, SQL, jQuery. Committed to delivering efficient, high-quality solutions by simplifying complex projects with technical expertise and innovative thinking.
Reply