LINQ(Language Integrated Query,语言集成查询),C# 的声明式查询语言,是 C# 最具革命性的特性之一。它让你可以用类似 SQL 的语法来查询任何数据源,而不仅仅是数据库。
🎯 LINQ 是什么?
LINQ = SQL 风格的查询语法 + C# 的类型安全 + 编译时检查
cs
// LINQ 查询示例:看起来像SQL,但完全是C#代码
var result = from book in books
where book.Price > 50
orderby book.Title
select book.Title;
📊 LINQ 的三种形式
1. 查询语法 (Query Syntax) - 类似SQL
cs
// 最直观,适合熟悉SQL的开发者
var cheapBooks = from book in books
where book.Price < 30
orderby book.Title descending
select new { book.Title, book.Author };
2. 方法语法 (Method Syntax) - Lambda表达式
cs
// 更灵活,是实际编译的代码
var cheapBooks = books
.Where(book => book.Price < 30)
.OrderByDescending(book => book.Title)
.Select(book => new { book.Title, book.Author });
3. 混合语法
cs
var count = (from book in books
where book.Price > 50
select book).Count();
🔄 LINQ 提供程序(不同的数据源)
cs
// 1. LINQ to Objects - 查询内存中的集合
List<Book> books = GetBooks();
var query = books.Where(b => b.Price > 50);
// 2. LINQ to Entities (EF Core) - 查询数据库
var dbQuery = context.Books.Where(b => b.Price > 50);
// 3. LINQ to XML - 查询XML文档
XDocument doc = XDocument.Load("books.xml");
var xmlQuery = from b in doc.Descendants("book")
select b.Element("title").Value;
// 4. LINQ to JSON (Newtonsoft.Json) - 查询JSON
JArray jsonArray = JArray.Parse(jsonString);
var jsonQuery = jsonArray.Where(j => (int)j["price"] > 50);
📝 LINQ 核心操作详解
1. 筛选数据 (Filtering)
cs
// Where - 基础筛选
var expensiveBooks = books.Where(b => b.Price > 100);
// 多个条件
var specialBooks = books.Where(b =>
b.Price > 50 &&
b.IsAvailable &&
b.Author.Contains("King"));
// OfType - 筛选特定类型
object[] objects = { 1, "hello", 2.5, "world" };
var strings = objects.OfType<string>(); // 只返回 "hello", "world"
2. 排序数据 (Ordering)
cs
// 单字段排序
var sortedByTitle = books.OrderBy(b => b.Title);
var sortedByPriceDesc = books.OrderByDescending(b => b.Price);
// 多字段排序
var multiSorted = books
.OrderBy(b => b.Category) // 先按类别
.ThenBy(b => b.Price) // 再按价格
.ThenByDescending(b => b.Rating); // 最后按评分降序
// 自定义比较器
var customSorted = books.OrderBy(b => b, new BookComparer());
3. 投影数据 (Projection)
cs
// Select - 转换数据
var bookTitles = books.Select(b => b.Title);
var bookInfo = books.Select(b => new
{
Name = b.Title,
Author = b.Author,
PriceWithTax = b.Price * 1.1m
});
// SelectMany - 扁平化嵌套集合
var allAuthors = books.SelectMany(b => b.Authors);
// 假设每本书有多个作者,结果会是所有作者的扁平列表
4. 分组数据 (Grouping)
cs
// GroupBy - 分组查询
var booksByAuthor = books.GroupBy(b => b.Author);
foreach (var group in booksByAuthor)
{
Console.WriteLine($"作者: {group.Key}");
Console.WriteLine($"作品数量: {group.Count()}");
foreach (var book in group)
{
Console.WriteLine($" - {book.Title}");
}
}
// 复杂分组
var complexGroup = books.GroupBy(b => new
{
b.Author,
Year = b.PublishDate.Year
});
// 分组后聚合
var statsByAuthor = books.GroupBy(b => b.Author)
.Select(g => new
{
Author = g.Key,
BookCount = g.Count(),
AvgPrice = g.Average(b => b.Price),
TotalSales = g.Sum(b => b.Sales)
});
5. 连接数据 (Joining)
cs
// 内连接
var bookDetails = from book in books
join author in authors
on book.AuthorId equals author.Id
select new
{
book.Title,
AuthorName = author.Name,
book.Price
};
// 左外连接
var leftJoin = from book in books
join author in authors
on book.AuthorId equals author.Id into authorGroup
from author in authorGroup.DefaultIfEmpty()
select new
{
book.Title,
AuthorName = author?.Name ?? "未知作者"
};
// 方法语法连接
var methodJoin = books.Join(authors,
book => book.AuthorId,
author => author.Id,
(book, author) => new { book.Title, author.Name });
6. 聚合操作 (Aggregation)
cs
// 基本聚合
var count = books.Count();
var totalPrice = books.Sum(b => b.Price);
var avgPrice = books.Average(b => b.Price);
var maxPrice = books.Max(b => b.Price);
var minPrice = books.Min(b => b.Price);
// 条件聚合
var availableCount = books.Count(b => b.IsAvailable);
var expensiveTotal = books.Where(b => b.Price > 100).Sum(b => b.Price);
// 分组聚合
var authorStats = books.GroupBy(b => b.Author)
.Select(g => new
{
Author = g.Key,
Books = g.Count(),
TotalValue = g.Sum(b => b.Price * b.Stock)
});
7. 分页和分区 (Paging)
cs
// 分页查询
int pageSize = 10;
int pageNumber = 2;
var page = books
.Skip((pageNumber - 1) * pageSize) // 跳过前面的记录
.Take(pageSize) // 取指定数量
.ToList();
// 分区操作
var first5 = books.Take(5); // 前5个
var last5 = books.TakeLast(5); // 最后5个
var skip5 = books.Skip(5); // 跳过前5个
var skipLast5 = books.SkipLast(5); // 跳过最后5个
8. 集合操作 (Set Operations)
cs
List<int> set1 = new() { 1, 2, 3, 4, 5 };
List<int> set2 = new() { 3, 4, 5, 6, 7 };
var distinct = set1.Distinct(); // 去重: 1,2,3,4,5
var union = set1.Union(set2); // 并集: 1,2,3,4,5,6,7
var intersect = set1.Intersect(set2); // 交集: 3,4,5
var except = set1.Except(set2); // 差集: 1,2
var concat = set1.Concat(set2); // 连接: 1,2,3,4,5,3,4,5,6,7
9. 元素操作 (Element Operations)
cs
// 获取单个元素
var first = books.First(); // 第一个
var firstOrDefault = books.FirstOrDefault(); // 第一个或默认值
var last = books.Last(); // 最后一个
var single = books.Single(b => b.Id == 1); // 唯一匹配
var elementAt = books.ElementAt(2); // 第三个元素
// 带条件的元素操作
var firstExpensive = books.First(b => b.Price > 100);
var lastAvailable = books.LastOrDefault(b => b.IsAvailable);
10. 量化操作 (Quantifiers)
cs
bool anyExpensive = books.Any(b => b.Price > 1000); // 是否有价格>1000的书
bool allAvailable = books.All(b => b.IsAvailable); // 是否所有书都可借
bool contains = books.Any(b => b.Title == "C#入门"); // 是否包含特定书
🎨 LINQ 查询语法 vs 方法语法
等价示例对比
cs
// 查询语法
var query1 = from book in books
where book.Price > 50
orderby book.Title
select new { book.Title, book.Price };
// 方法语法
var query2 = books
.Where(book => book.Price > 50)
.OrderBy(book => book.Title)
.Select(book => new { book.Title, book.Price });
只能使用方法语法的操作
cs
// 1. 聚合操作
var count = books.Count(b => b.Price > 50);
// 2. 元素操作
var first = books.First();
// 3. 转换操作
var dictionary = books.ToDictionary(b => b.Id);
var lookup = books.ToLookup(b => b.Category);
// 4. 其他操作
var reversed = books.Reverse();
var zip = books.Zip(authors, (b, a) => new { b.Title, a.Name });
🔧 LINQ 在 EF Core 中的特殊用法
1. 延迟执行 (Deferred Execution)
cs
// 查询定义(不执行)
var query = context.Books.Where(b => b.Price > 50);
// 查询仍未执行
query = query.OrderBy(b => b.Title);
// 触发执行的时机:
var list = query.ToList(); // 1. 转换为列表
var array = query.ToArray(); // 2. 转换为数组
var first = query.First(); // 3. 获取第一个元素
foreach (var item in query) // 4. 遍历时
{
// 在这里,查询才会真正执行
}
2. IQueryable 与 IEnumerable 的区别
cs
// IQueryable - 在数据库执行查询
IQueryable<Book> dbQuery = context.Books;
var result1 = dbQuery.Where(b => b.Price > 50).ToList();
// SQL: SELECT * FROM Books WHERE Price > 50
// IEnumerable - 在内存中执行查询
IEnumerable<Book> memoryQuery = context.Books.AsEnumerable();
var result2 = memoryQuery.Where(b => b.Price > 50).ToList();
// SQL: SELECT * FROM Books
// 然后在内存中过滤(性能差!)
3. 客户端评估 vs 服务器评估
cs
// ✅ 服务器端评估(在数据库执行)
var books1 = context.Books
.Where(b => b.Price > 50) // 转换为SQL WHERE条件
.ToList();
// ⚠️ 客户端评估(在内存中执行,可能性能差)
var books2 = context.Books
.ToList() // 先获取所有数据
.Where(b => b.Price > 50); // 在内存中过滤
// ❌ 无法转换为SQL的操作会引发异常
var books3 = context.Books
.Where(b => b.Title.StartsWith("C#")) // ✅ 可以转换
.Where(b => SomeMethod(b.Price)) // ❌ 无法转换为SQL
.ToList();
📚 实际应用示例
1. 电商产品查询
cs
public IEnumerable<Product> SearchProducts(
string keyword,
decimal? minPrice,
decimal? maxPrice,
string category,
int page = 1)
{
var query = context.Products.AsQueryable();
if (!string.IsNullOrEmpty(keyword))
{
query = query.Where(p =>
p.Name.Contains(keyword) ||
p.Description.Contains(keyword));
}
if (minPrice.HasValue)
query = query.Where(p => p.Price >= minPrice.Value);
if (maxPrice.HasValue)
query = query.Where(p => p.Price <= maxPrice.Value);
if (!string.IsNullOrEmpty(category))
query = query.Where(p => p.Category == category);
return query
.OrderBy(p => p.Name)
.Skip((page - 1) * PageSize)
.Take(PageSize)
.Select(p => new ProductDto
{
Id = p.Id,
Name = p.Name,
Price = p.Price,
Category = p.Category,
Stock = p.Stock
});
}
2. 报表统计
cs
public SalesReport GenerateMonthlyReport(int year, int month)
{
var report = context.Orders
.Where(o => o.OrderDate.Year == year && o.OrderDate.Month == month)
.GroupBy(o => o.ProductId)
.Select(g => new ProductSales
{
ProductId = g.Key,
ProductName = g.First().Product.Name,
TotalQuantity = g.Sum(o => o.Quantity),
TotalRevenue = g.Sum(o => o.Quantity * o.UnitPrice),
AveragePrice = g.Average(o => o.UnitPrice)
})
.OrderByDescending(p => p.TotalRevenue)
.ToList();
return new SalesReport
{
Month = month,
Year = year,
TotalRevenue = report.Sum(r => r.TotalRevenue),
TopProducts = report.Take(10).ToList()
};
}
💡 LINQ 最佳实践
1. 性能优化
cs
// ❌ 不好:多次迭代
var expensiveBooks = books.Where(b => b.Price > 100);
int count = expensiveBooks.Count(); // 第一次迭代
var list = expensiveBooks.ToList(); // 第二次迭代
// ✅ 好:缓存结果
var expensiveBooksList = books
.Where(b => b.Price > 100)
.ToList(); // 一次执行
int count = expensiveBooksList.Count; // 使用缓存
2. 合理使用 Select
cs
// ❌ 不好:选择过多数据
var allData = context.Books.ToList(); // 获取所有字段
var titles = allData.Select(b => b.Title); // 在内存中投影
// ✅ 好:只选择需要的字段
var titles = context.Books
.Select(b => b.Title) // 在数据库层面投影
.ToList();
3. 避免在循环中使用 LINQ
cs
// ❌ 不好:N+1查询问题
foreach (var author in authors)
{
var books = context.Books
.Where(b => b.AuthorId == author.Id) // 每次循环都查询
.ToList();
}
// ✅ 好:一次查询
var authorBooks = context.Books
.GroupBy(b => b.AuthorId) // 一次分组查询
.ToDictionary(g => g.Key, g => g.ToList());
🔍 Java 开发者对照表
