| name | csharp-linq |
| user-invocable | false |
| description | Use when lINQ (Language Integrated Query) with query and method syntax, deferred execution, expression trees, and performance optimization. |
| allowed-tools | ["Read","Write","Edit","Grep","Glob","Bash"] |
C# LINQ
LINQ (Language Integrated Query) provides a consistent query experience across
different data sources including collections, databases, XML, and more. It
combines the power of SQL-like queries with C# type safety and IntelliSense
support, enabling expressive and maintainable data manipulation code.
Query Syntax
Query syntax provides SQL-like syntax for querying data sources, compiled to
method calls at compile time.
using System;
using System.Collections.Generic;
using System.Linq;
public class QuerySyntaxExamples
{
public record Person(string Name, int Age, string City);
public IEnumerable<Person> BasicQuery(List<Person> people)
{
var query = from p in people
where p.Age >= 18
select p;
return query;
}
public IEnumerable<Person> MultipleConditions(List<Person> people)
{
var query = from p in people
where p.Age >= 18 && p.City == "Seattle"
orderby p.Name
select p;
return query;
}
public IEnumerable<string> ProjectNames(List<Person> people)
{
var query = from p in people
where p.Age >= 21
select p.Name;
return query;
}
public IEnumerable<object> AnonymousProjection(List<Person> people)
{
var query = from p in people
select new
{
p.Name,
p.Age,
IsAdult = p.Age >= 18
};
return query;
}
public IEnumerable<IGrouping<string, Person>> GroupByCity(
List<Person> people)
{
var query = from p in people
group p by p.City;
return query;
}
public IEnumerable<object> GroupWithProjection(List<Person> people)
{
var query = from p in people
group p by p.City into cityGroup
select new
{
City = cityGroup.Key,
Count = cityGroup.Count(),
AverageAge = cityGroup.Average(p => p.Age)
};
return query;
}
public record Order(int Id, string PersonName, decimal Amount);
public IEnumerable<object> JoinExample(
List<Person> people,
List<Order> orders)
{
var query = from p in people
join o in orders on p.Name equals o.PersonName
select new
{
p.Name,
p.Age,
OrderAmount = o.Amount
};
return query;
}
public IEnumerable<object> LeftJoin(
List<Person> people,
List<Order> orders)
{
var query = from p in people
join o in orders on p.Name equals o.PersonName
into personOrders
from po in personOrders.DefaultIfEmpty()
select new
{
p.Name,
OrderAmount = po?.Amount ?? 0
};
return query;
}
}
Method Syntax
Method syntax uses extension methods for querying, providing more flexibility
and access to all LINQ operators.
using System;
using System.Collections.Generic;
using System.Linq;
public class MethodSyntaxExamples
{
public record Product(string Name, decimal Price, string Category);
public IEnumerable<Product> FilterProducts(List<Product> products)
{
return products
.Where(p => p.Price > 100)
.Where(p => p.Category == "Electronics");
}
public IEnumerable<Product> OrderProducts(List<Product> products)
{
return products
.OrderBy(p => p.Category)
.ThenByDescending(p => p.Price);
}
public IEnumerable<string> ProjectNames(List<Product> products)
{
return products
.Select(p => p.Name.ToUpper());
}
public IEnumerable<int> FlattenLists()
{
var lists = new List<List<int>>
{
new List<int> { 1, 2, 3 },
List<> { , },
List<> { , , , }
};
lists.SelectMany(list => list);
}
IEnumerable<IGrouping<, Product>> GroupByCategory(
List<Product> products)
{
products.GroupBy(p => p.Category);
}
{
total = products.Sum(p => p.Price);
average = products.Average(p => p.Price);
max = products.Max(p => p.Price);
min = products.Min(p => p.Price);
count = products.Count();
expensiveCount = products.Count(p => p.Price > );
}
{
hasExpensive = products.Any(p => p.Price > );
allAffordable = products.All(p => p.Price < );
hasElectronics = products.Any(p =>
p.Category == );
}
{
products
.OrderBy(p => p.Name)
.Skip((page - ) * pageSize)
.Take(pageSize);
}
{
products
.Select(p => p.Category)
.Distinct();
}
{
union = products1.Union(products2);
intersect = products1.Intersect(products2);
except = products1.Except(products2);
}
}
Deferred Execution
LINQ queries use deferred execution, meaning the query executes when
enumerated, not when defined.
using System;
using System.Collections.Generic;
using System.Linq;
public class DeferredExecutionExamples
{
public void DeferredExecutionDemo()
{
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var query = numbers.Where(n => n > 2);
Console.WriteLine("Before modification:");
foreach (var n in query)
{
Console.WriteLine(n);
}
numbers.Add(6);
numbers.Add(7);
Console.WriteLine("After modification:");
foreach (var n in query)
{
Console.WriteLine(n);
}
}
public void ImmediateExecution()
{
var numbers = new List<int> { 1, 2, 3, , };
list = numbers.Where(n => n > ).ToList();
numbers.Add();
numbers.Add();
( n list)
{
Console.WriteLine(n);
}
}
{
numbers = List<> { , , , , };
array = numbers.Where(n => n > ).ToArray();
dict = numbers.ToDictionary(n => n, n => n * );
hashSet = numbers.ToHashSet();
lookup = numbers.ToLookup(n => n % );
count = numbers.Count(n => n > );
sum = numbers.Sum();
max = numbers.Max();
any = numbers.Any(n => n > );
}
{
numbers = GetNumbers();
count = numbers.Count();
sum = numbers.Sum();
list = numbers.ToList();
count = list.Count;
sum = list.Sum();
}
{
Console.WriteLine();
( i = ; i <= ; i++)
{
i;
}
}
}
Complex Queries
Combining multiple LINQ operations for complex data transformations.
using System;
using System.Collections.Generic;
using System.Linq;
public class ComplexQueries
{
public record Student(string Name, int Grade, string Subject,
int Score);
public record Course(string Subject, string Teacher, int Credits);
public IEnumerable<object> StudentReport(
List<Student> students,
List<Course> courses)
{
return students
.Where(s => s.Grade >= 10)
.GroupBy(s => new { s.Name, s.Grade })
.Select(g => new
{
g.Key.Name,
g.Key.Grade,
Subjects = g.Select(s => s.Subject).Distinct(),
AverageScore = g.Average(s => s.Score),
TotalCredits = g.Join(
courses,
s => s.Subject,
c => c.Subject,
(s, c) => c.Credits
).Sum()
})
.OrderByDescending(s => s.AverageScore);
}
public IEnumerable<object> TopStudentsBySubject(
List<Student> students)
{
return students
.GroupBy(s => s.Subject)
.Select(g => new
{
Subject = g.Key,
TopStudent = g
.OrderByDescending(s => s.Score)
.Select(s => { s.Name, s.Score })
.FirstOrDefault(),
ClassAverage = g.Average(s => s.Score)
});
}
{
students
.OrderBy(s => s.Name)
.ThenBy(s => s.Subject)
.Select((s, index) =>
{
s.Name,
s.Subject,
s.Score,
RunningTotal = students
.Take(index + )
.Sum(x => x.Score)
});
}
;
{
categories
.Where(c => c.Parent == )
.Select(parent =>
{
parent.Name,
Children = categories
.Where(c => c.Parent == parent.Name)
.Select(child =>
{
child.Name,
Grandchildren = categories
.Where(gc => gc.Parent == child.Name)
})
});
}
}
Performance Optimization
Understanding LINQ performance characteristics and optimization techniques.
using System;
using System.Collections.Generic;
using System.Linq;
public class PerformanceOptimization
{
public void CountOptimization(List<int> numbers)
{
int count1 = numbers.Where(n => n > 0).Count();
int count2 = numbers.Count;
bool hasItems = numbers.Any();
bool hasItems2 = numbers.Count() > 0;
}
public void AvoidMultipleEnumerations()
{
var query = GetExpensiveQuery();
int count = query.Count();
int sum = query.Sum();
var first = query.First();
var list = query.ToList();
count = list.Count;
sum = list.Sum();
first = list.First();
}
public IEnumerable<string> FilterBeforeProject(List<int> numbers)
{
numbers
.Where(n => n > )
.Select(n => n.ToString());
}
? FindFirst(List<> numbers)
{
numbers.FirstOrDefault(n => n > );
}
{
numbers
.OrderByDescending(n => n)
.Take();
}
{
numbers
.AsParallel()
.Where(n => ExpensiveOperation(n))
.Select(n => n * );
}
{
Enumerable.Range(, )
.Where(n => n % == );
}
{
System.Threading.Thread.Sleep();
n > ;
}
}
LINQ to Objects vs LINQ to SQL
Understanding differences between in-memory and database queries.
using System;
using System.Collections.Generic;
using System.Linq;
public class LinqProviders
{
public record Customer(int Id, string Name, string City);
public void LinqToObjects(List<Customer> customers)
{
var query = customers
.Where(c => c.City == "Seattle")
.OrderBy(c => c.Name)
.Select(c => new { c.Name, c.City });
var withMethods = customers
.Where(c => IsValidCity(c.City))
.ToList();
}
public void LinqToSQL()
{
}
{
!.IsNullOrEmpty(city) && city.Length > ;
}
}
Expression Trees
Understanding expression trees for advanced LINQ scenarios.
using System;
using System.Linq.Expressions;
public class ExpressionTreeExamples
{
public void BuildExpressionTree()
{
ParameterExpression param = Expression.Parameter(typeof(int), "x");
BinaryExpression body = Expression.Add(
param,
Expression.Constant(5)
);
Expression<Func<int, int>> expr =
Expression.Lambda<Func<int, int>>(body, param);
Func<int, int> func = expr.Compile();
int result = func(10);
}
public void LambdaToExpression()
{
Expression<Func<int, bool>> expr = x => x > 5;
Func<int, bool> func = expr.Compile();
bool result = func(10);
}
public void AnalyzeExpression()
{
Expression<Func<int, >> expr = x => x > ;
lambda = (LambdaExpression)expr;
body = (BinaryExpression)lambda.Body;
left = (ParameterExpression)body.Left;
right = (ConstantExpression)body.Right;
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
}
{
ParameterExpression param = Expression.Parameter((T), );
MemberExpression property = Expression.Property(param,
propertyName);
ConstantExpression constant = Expression.Constant();
BinaryExpression equal = Expression.Equal(property, constant);
Expression.Lambda<Func<T, >>(equal, param);
}
}
Best Practices
- Use method syntax for complex queries with multiple operations
- Use query syntax for queries that look more like SQL
- Call
ToList() or ToArray() when you need to enumerate multiple times
- Use
Any() instead of Count() > 0 for existence checks
- Filter with
Where() before projecting with Select()
- Use
FirstOrDefault() instead of Where().First() when finding single item
- Avoid
Select() when you don't need to transform the data
- Use
AsParallel() only for CPU-intensive operations on large datasets
- Be aware of deferred execution and when queries actually execute
- Consider expression tree compilation cost for frequently-used queries
Common Pitfalls
- Multiple enumeration of
IEnumerable causing performance issues
- Using LINQ on database queries without understanding SQL translation
- Calling
ToList() too early, losing deferred execution benefits
- Using
Count() method instead of Count property on collections
- Not disposing
IEnumerable from database queries, leaking connections
- Using LINQ for simple loops where foreach would be clearer
- Excessive
AsParallel() causing overhead instead of speedup
- Capturing variables in lambda expressions causing unintended closures
- Using
Select() where SelectMany() is needed for flattening
- Not understanding operator precedence in complex query expressions
When to Use LINQ
Use LINQ when you need:
- Querying collections, databases, XML, or other data sources uniformly
- Expressive data transformation and filtering operations
- Type-safe queries with compile-time checking and IntelliSense
- Functional programming patterns for data manipulation
- Complex grouping, joining, and aggregation operations
- Declarative code that expresses intent clearly
- Integration with Entity Framework or other ORMs
- Composition of queries from reusable components
- Parallel processing of large datasets with PLINQ
- Consistent API across different data sources
Resources