LINQ 是 .NET 框架中一项革命性的技术,它将查询功能直接集成到 C# 和 VB.NET 语言中。通过 LINQ,开发者可以使用统一的语法操作不同类型的数据源,无需学习特定于每种数据源的查询语言。
### LINQ 的核心概念
#### 1. **查询表达式**
LINQ 查询表达式类似于 SQL,但针对对象模型设计。基本结构包括:
- `from` 子句:指定数据源和范围变量
- `where` 子句:筛选条件
- `select` 或 `group by` 子句:投影或分组结果
```csharp
// 查询所有成年用户并按姓名排序
var adults = from user in users
where user.Age >= 18
orderby user.Name
select user;
```
#### 2. **标准查询操作符**
LINQ 提供了一组丰富的扩展方法,称为"标准查询操作符",可用于各种数据处理任务:
```csharp
// 筛选、投影、排序、聚合示例
var result = users
.Where(u => u.Age > 25) // 筛选
.Select(u => new { // 投影(匿名类型)
FullName = u.FirstName + " " + u.LastName,
BirthYear = DateTime.Now.Year - u.Age
})
.OrderBy(u => u.BirthYear) // 排序
.Take(10) // 分页
.ToList(); // 转换为列表
```
#### 3. **延迟执行与立即执行**
- **延迟执行**:大多数 LINQ 查询在定义时不会执行,而是在迭代结果时执行(如 foreach 循环)
- **立即执行**:使用 `ToList()`、`ToArray()`、`Count()` 等方法会立即执行查询并返回结果
#### 4. **表达式树**
LINQ to Entities 等提供程序使用表达式树将 LINQ 查询转换为数据库命令。这使得查询可以在服务器端执行,而不是在客户端加载所有数据后再筛选。
### LINQ 提供程序
LINQ 支持多种数据源,每种都有对应的提供程序:
1. **LINQ to Objects**
- 用于查询内存中的集合(如 List、Array、Dictionary)
- 示例:筛选列表中的元素、计算平均值等
2. **LINQ to Entities**
- 与 Entity Framework 集成,用于查询数据库
- 支持关系型数据库(SQL Server、MySQL、PostgreSQL 等)
- 自动处理对象关系映射(ORM)
3. **LINQ to XML (XLinq)**
- 用于查询和操作 XML 文档
- 比传统的 XmlDocument API 更简洁
- 支持 XPath 类似的查询
4. **LINQ to DataSet**
- 用于查询 ADO.NET DataSet 和 DataTable
- 提供类型安全的访问方式
5. **第三方提供程序**
- LINQ to SQL(微软早期 ORM,现已被 EF 取代)
- LINQ to JSON(如 Newtonsoft.Json.Linq)
- LINQ to NHibernate
- LINQ to SharePoint
- LINQ to Cosmos DB
### LINQ 查询语法详解
#### 1. **基本查询结构**
```csharp
// 基本查询表达式
var results = from element in source
where condition
orderby property ascending/descending
select element;
// 等效的方法链语法
var results = source
.Where(element => condition)
.OrderBy(element => property)
.Select(element => element);
```
#### 2. **投影操作**
- `Select()`:将元素转换为新形式(如提取属性或创建匿名类型)
- `SelectMany()`:将集合的集合"展平"为单个集合
```csharp
// 投影为匿名类型
var namesAndAges = users.Select(u => new { u.Name, u.Age });
// 展平操作示例
var allBooks = authors.SelectMany(a => a.Books);
```
#### 3. **筛选操作**
- `Where()`:基于条件筛选元素
- `OfType<T>()`:筛选特定类型的元素
```csharp
// 多条件筛选
var filtered = numbers.Where(n => n > 10 && n < 100 && n % 2 == 0);
// 类型筛选
var stringsOnly = mixedCollection.OfType<string>();
```
#### 4. **排序操作**
- `OrderBy()/OrderByDescending()`:一级排序
- `ThenBy()/ThenByDescending()`:多级排序
- `Reverse()`:反转顺序
```csharp
// 多级排序
var sorted = products
.OrderBy(p => p.Category)
.ThenByDescending(p => p.Price);
```
#### 5. **分组与聚合**
- `GroupBy()`:按指定键分组
- `Aggregate()`:自定义聚合操作
- `Count()/Sum()/Average()/Min()/Max()`:标准聚合函数
```csharp
// 按部门分组并计算平均工资
var deptAvgSalaries = employees
.GroupBy(e => e.Department)
.Select(g => new {
Department = g.Key,
AverageSalary = g.Average(e => e.Salary)
});
// 自定义聚合示例
var totalLength = words.Aggregate(0, (sum, word) => sum + word.Length);
```
#### 6. **连接操作**
- `Join()`:内连接
- `GroupJoin()`:左外连接
- `Zip()`:将两个序列的对应元素配对
```csharp
// 内连接示例
var joined = customers.Join(orders,
c => c.CustomerId,
o => o.CustomerId,
(c, o) => new { Customer = c.Name, Order = o.OrderDate });
// 左外连接示例
var allCustomersWithOrders = customers.GroupJoin(orders,
c => c.CustomerId,
o => o.CustomerId,
(c, os) => new { Customer = c, Orders = os });
```
#### 7. **集合操作**
- `Distinct()`:移除重复项
- `Union()/Intersect()/Except()`:集合运算
- `SequenceEqual()`:比较两个序列是否相等
```csharp
// 集合运算示例
var uniqueNumbers = numbers.Distinct();
var allUnique = list1.Union(list2);
var commonItems = list1.Intersect(list2);
var onlyInList1 = list1.Except(list2);
```
#### 8. **分区操作**
- `Take()/Skip()`:分页功能
- `TakeWhile()/SkipWhile()`:条件分区
```csharp
// 分页示例
var page2 = items.Skip(10).Take(10);
// 条件分区示例
var firstPositive = numbers.TakeWhile(n => n > 0);
```
### LINQ 高级特性
#### 1. **匿名类型**
在投影操作中创建临时对象,无需显式定义类:
```csharp
var results = products.Select(p => new {
p.Name,
p.Price,
DiscountedPrice = p.Price * 0.9
});
```
#### 2. **复合键**
在分组和连接操作中使用多个属性作为键:
```csharp
// 按多属性分组
var groups = employees.GroupBy(e => new { e.Department, e.Location });
// 多属性连接
var joined = table1.Join(table2,
t1 => new { t1.Key1, t1.Key2 },
t2 => new { t2.Key1, t2.Key2 },
(t1, t2) => new { t1, t2 });
```
#### 3. **导航属性查询**
在 Entity Framework 中通过导航属性进行关联查询:
```csharp
// 查询订单及其客户信息
var ordersWithCustomers = context.Orders
.Include(o => o.Customer)
.Where(o => o.OrderDate > DateTime.Now.AddDays(-30));
```
#### 4. **动态 LINQ**
使用字符串构建动态查询(适用于需要在运行时构建查询的场景):
```csharp
// 使用 System.Linq.Dynamic.Core 库
var propertyName = "Price";
var sortDirection = "Descending";
var query = products.AsQueryable().OrderBy($"{propertyName} {sortDirection}");
```
### LINQ 性能考虑
1. **延迟执行优化**:避免不必要的中间集合,利用链式查询减少内存使用
2. **数据库查询优化**:
- 尽量在数据库端完成筛选和排序(使用 LINQ to Entities)
- 避免在 LINQ 查询中调用客户端方法(会导致数据全部加载到客户端)
- 使用 `AsNoTracking()` 提高只读查询性能
3. **集合操作优化**:
- 使用 `HashSet<T>` 提高 `Contains()` 操作性能
- 对于大数据集,考虑使用并行 LINQ(PLINQ)
```csharp
// 并行 LINQ 示例
var parallelResult = numbers
.AsParallel()
.Where(n => n % 2 == 0)
.Select(n => n * 2)
.ToList();
```
### LINQ 最佳实践
1. **选择合适的语法**:
- 简单查询使用查询表达式语法
- 复杂查询(嵌套、方法链)使用方法链语法
2. **尽早筛选数据**:
```csharp
// 好的做法:先筛选再投影
var result = data.Where(d => d.Condition).Select(d => d.Property);
// 不好的做法:先投影再筛选
var result = data.Select(d => d.Property).Where(p => p.Condition);
```
3. **避免过度使用 LINQ**:
- 简单的循环可能比复杂的 LINQ 查询更清晰
- 对于简单的集合遍历,传统 foreach 可能更高效
4. **使用有意义的变量名**:
```csharp
// 好的做法
var activeCustomers = customers.Where(c => c.IsActive);
// 不好的做法
var x = customers.Where(c => c.IsActive);
```
### LINQ 与 Entity Framework 的集成
LINQ 与 EF 的结合是 .NET 数据访问的强大组合:
```csharp
// 使用 EF Core 和 LINQ 查询数据库
using (var context = new ApplicationDbContext())
{
// 筛选、排序和分页
var products = context.Products
.Where(p => p.Price > 100)
.OrderBy(p => p.Name)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToList();
// 关联数据查询
var ordersWithDetails = context.Orders
.Include(o => o.Customer)
.Include(o => o.OrderItems)
.ThenInclude(oi => oi.Product)
.Where(o => o.OrderDate > DateTime.Now.AddMonths(-1))
.ToList();
}
```
### 总结
LINQ 是 .NET 框架中最强大的功能之一,它统一了不同数据源的查询方式,提高了代码的可读性和可维护性。通过掌握 LINQ 的各种操作符和使用场景,可以显著提升 .NET 开发效率,尤其是在数据处理和数据库交互方面。
1万+

被折叠的 条评论
为什么被折叠?



