Skip to content

Bits of .NET

Daily micro-tips for C#, SQL, performance, and scalable backend engineering.

  • Asp.Net Core
  • C#
  • SQL
  • JavaScript
  • CSS
  • About
  • ErcanOPAK.com
  • No Access
  • Privacy Policy
C#

C# LINQ Performance Secret: How ToQueryString() Can Reduce Database Calls by 90%

- 05.02.26 - ErcanOPAK

Are your Entity Framework queries making too many database trips? Discover how IQueryable.ToString() reveals the actual SQL and helps you batch queries efficiently.

The Hidden Debugging Gem:

// Instead of this naive approach (multiple queries)
var users = db.Users.Where(u => u.IsActive).ToList();
var orders = db.Orders.Where(o => o.UserId == userId).ToList();
var products = db.Products.Where(p => p.CategoryId == categoryId).ToList();

// Each ToList() executes separate SQL query = 3 database trips!

// Use ToQueryString() to see what's happening
var query = db.Users.Where(u => u.IsActive);
Console.WriteLine(query.ToQueryString());

// Output shows the generated SQL BEFORE execution
// This helps you identify if filters are applied in database or memory

Batch Multiple Queries into One:

// Problem: N+1 query issue
var products = db.Products.ToList();
foreach (var product in products)
{
    var category = db.Categories.FirstOrDefault(c => c.Id == product.CategoryId);
    // Each iteration makes database call!
}

// Solution: Use Include() and projection
var optimizedQuery = db.Products
    .Include(p => p.Category)
    .Select(p => new 
    {
        p.Name,
        CategoryName = p.Category.Name,
        p.Price
    })
    .ToQueryString(); // Check the SQL first!

// Execute optimized version
var result = db.Products
    .Include(p => p.Category)
    .Select(p => new 
    {
        p.Name,
        CategoryName = p.Category.Name,
        p.Price
    })
    .ToList();

// Single SQL query with JOIN instead of N+1 queries

Debug Complex Queries:

// Complex query with multiple conditions
var complexQuery = db.Orders
    .Where(o => o.Status == OrderStatus.Completed)
    .Where(o => o.CreatedDate >= DateTime.UtcNow.AddMonths(-1))
    .GroupBy(o => o.UserId)
    .Select(g => new 
    {
        UserId = g.Key,
        TotalAmount = g.Sum(o => o.Amount),
        OrderCount = g.Count()
    })
    .Where(x => x.TotalAmount > 1000);

// See the generated SQL without executing
var sql = complexQuery.ToQueryString();
Console.WriteLine(sql);

// You'll discover if grouping happens in SQL (good) or memory (bad)
// This helps optimize database indexes

Combine Multiple Conditions Efficiently:

// Instead of multiple OR conditions (slow)
var searchTerms = new[] { "laptop", "computer", "notebook" };
var query = db.Products.AsQueryable();

foreach (var term in searchTerms)
{
    query = query.Where(p => p.Name.Contains(term));
}
// Generates: WHERE Name LIKE '%laptop%' OR Name LIKE '%computer%' ...

// Better: Build dynamic expression tree
var parameter = Expression.Parameter(typeof(Product), "p");
var property = Expression.Property(parameter, "Name");
var constant = Expression.Constant("laptop");
var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });

var expressions = searchTerms.Select(term =>
    Expression.Call(property, containsMethod, Expression.Constant(term)));

var body = expressions.Aggregate(Expression.OrElse);
var lambda = Expression.Lambda>(body, parameter);

var optimizedResult = db.Products.Where(lambda);
Console.WriteLine(optimizedResult.ToQueryString());

Real-World Example – E-commerce Report:

// Before optimization (5 separate queries)
public ReportDto GetMonthlyReport(int year, int month)
{
    var totalOrders = db.Orders
        .Where(o => o.Date.Year == year && o.Date.Month == month)
        .Count();
    
    var totalRevenue = db.Orders
        .Where(o => o.Date.Year == year && o.Date.Month == month)
        .Sum(o => o.Amount);
    
    var topProducts = db.OrderItems
        .Where(oi => oi.Order.Date.Year == year && oi.Order.Date.Month == month)
        .GroupBy(oi => oi.ProductId)
        .Select(g => new { ProductId = g.Key, Count = g.Count() })
        .OrderByDescending(x => x.Count)
        .Take(10)
        .ToList();
    
    // 3 database calls!
}

// After optimization (1 query)
public ReportDto GetMonthlyReportOptimized(int year, int month)
{
    var startDate = new DateTime(year, month, 1);
    var endDate = startDate.AddMonths(1);
    
    var query = db.Orders
        .Where(o => o.Date >= startDate && o.Date < endDate)
        .Select(o => new 
        {
            Order = o,
            Items = o.Items
        })
        .ToQueryString(); // Check SQL first!
    
    // Execute single optimized query
    var data = db.Orders
        .Where(o => o.Date >= startDate && o.Date < endDate)
        .Select(o => new 
        {
            Order = o,
            Items = o.Items
        })
        .ToList();
    
    // Process in memory - faster than multiple round trips
    var totalOrders = data.Count;
    var totalRevenue = data.Sum(d => d.Order.Amount);
    var topProducts = data.SelectMany(d => d.Items)
        .GroupBy(i => i.ProductId)
        .Select(g => new { ProductId = g.Key, Count = g.Count() })
        .OrderByDescending(x => x.Count)
        .Take(10)
        .ToList();
    
    return new ReportDto
    {
        TotalOrders = totalOrders,
        TotalRevenue = totalRevenue,
        TopProducts = topProducts
    };
}

Related posts:

Why async Code Can Still Block Threads

Why Async Controllers Still Block Threads

ASP.NET Core Startup Is Slow

Post Views: 2

Post navigation

C#: Use Source Generators to Generate Boilerplate Code at Compile Time
AI Prompt Engineering: How to Get Perfect Images Every Time with Midjourney

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

February 2026
M T W T F S S
 1
2345678
9101112131415
16171819202122
232425262728  
« Jan    

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (935)
  • How to add default value for Entity Framework migrations for DateTime and Bool (832)
  • Get the First and Last Word from a String or Sentence in SQL (826)
  • How to select distinct rows in a datatable in C# (799)
  • How to make theater mode the default for Youtube (716)
  • Add Constraint to SQL Table to ensure email contains @ (574)
  • How to enable, disable and check if Service Broker is enabled on a database in SQL Server (553)
  • Average of all values in a column that are not zero in SQL (520)
  • How to use Map Mode for Vertical Scroll Mode in Visual Studio (474)
  • Find numbers with more than two decimal places in SQL (436)

Recent Posts

  • C#: Use MemoryPack for 10x Faster Serialization than JSON
  • C#: Use params ReadOnlySpan for Allocation-Free Variable Arguments
  • C#: Use ObjectPool for Reusing Expensive Objects
  • C#: Use Lazy for Expensive Object Initialization
  • SQL: Use STRING_AGG to Concatenate Rows into Comma-Separated List
  • SQL: Use Filtered Indexes to Index Only Subset of Rows
  • .NET Core: Use Result Pattern to Avoid Exceptions for Expected Errors
  • .NET Core: Use IOptions Pattern for Strongly-Typed Configuration
  • Git: Use .gitattributes to Handle Line Endings Across OS
  • Git: Use git notes to Add Comments to Commits Without Changing History

Most Viewed Posts

  • Get the User Name and Domain Name from an Email Address in SQL (935)
  • How to add default value for Entity Framework migrations for DateTime and Bool (832)
  • Get the First and Last Word from a String or Sentence in SQL (826)
  • How to select distinct rows in a datatable in C# (799)
  • How to make theater mode the default for Youtube (716)

Recent Posts

  • C#: Use MemoryPack for 10x Faster Serialization than JSON
  • C#: Use params ReadOnlySpan for Allocation-Free Variable Arguments
  • C#: Use ObjectPool for Reusing Expensive Objects
  • C#: Use Lazy for Expensive Object Initialization
  • SQL: Use STRING_AGG to Concatenate Rows into Comma-Separated List

Social

  • ErcanOPAK.com
  • GoodReads
  • LetterBoxD
  • Linkedin
  • The Blog
  • Twitter
© 2026 Bits of .NET | Built with Xblog Plus free WordPress theme by wpthemespace.com