核心概念:ORM(对象关系映射)
cs
// C# LINQ 代码
var books = await _context.Books
.Where(b => b.Price > 50)
.OrderBy(b => b.Title)
.ToListAsync();
// 自动转换为 SQL 语句
// SELECT * FROM Books WHERE Price > 50 ORDER BY Title
LINQ 到 SQL 的转换机制
1. 查询方法分类
A. 立即执行方法 - 立即执行并返回结果
cs
// 立即执行,返回具体数据
.ToList() // 转换为列表
.ToArray() // 转换为数组
.First() // 获取第一个
.Single() // 获取唯一一个
.Count() // 计数
.Sum() // 求和
B. 延迟执行方法 - 构建查询,不立即执行
cs
// 只是构建查询,不执行SQL
.Where() // 条件过滤
.OrderBy() // 排序
.Select() // 选择特定字段
.Include() // 包含关联数据
.Skip() // 跳过记录
.Take() // 获取指定数量
2. 常见的LINQ方法及其SQL转换
cs
// 1. 基本查询
var books = await _context.Books.ToListAsync();
// SQL: SELECT * FROM Books
// 2. 条件查询
var expensiveBooks = await _context.Books
.Where(b => b.Price > 100 && b.IsAvailable)
.ToListAsync();
// SQL: SELECT * FROM Books WHERE Price > 100 AND IsAvailable = 1
// 3. 排序
var sortedBooks = await _context.Books
.OrderBy(b => b.Title)
.ThenByDescending(b => b.PublishDate)
.ToListAsync();
// SQL: SELECT * FROM Books ORDER BY Title ASC, PublishDate DESC
// 4. 分页
var pagedBooks = await _context.Books
.Skip(10) // 跳过前10条
.Take(5) // 取5条
.ToListAsync();
// SQL: SELECT * FROM Books ORDER BY Id OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY
// 5. 聚合查询
var totalPrice = await _context.Books
.Where(b => b.IsAvailable)
.SumAsync(b => b.Price);
// SQL: SELECT SUM(Price) FROM Books WHERE IsAvailable = 1
// 6. 分组查询
var booksByAuthor = await _context.Books
.GroupBy(b => b.Author)
.Select(g => new { Author = g.Key, Count = g.Count() })
.ToListAsync();
// SQL: SELECT Author, COUNT(*) as Count FROM Books GROUP BY Author
// 7. 连接查询
var bookDetails = await _context.Books
.Join(_context.Authors,
book => book.AuthorId,
author => author.Id,
(book, author) => new { book.Title, AuthorName = author.Name })
.ToListAsync();
// SQL: SELECT b.Title, a.Name as AuthorName
// FROM Books b INNER JOIN Authors a ON b.AuthorId = a.Id
EF Core 如何查看生成的SQL
1. 启用日志记录(最简单的方法)
cs
// Program.cs 中配置
builder.Services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(connectionString);
options.LogTo(Console.WriteLine, LogLevel.Information); // 输出到控制台
options.EnableSensitiveDataLogging(); // 显示参数值
});
2. 使用SQL Server Profiler
-
查看所有发送到数据库的SQL语句
-
包括参数值
3. 在调试时查看
cs
public async Task<List<Book>> GetAllBooksAsync()
{
var query = _context.Books.OrderBy(b => b.Title);
// 获取生成的SQL语句
var sql = query.ToQueryString();
Console.WriteLine(sql);
return await query.ToListAsync();
}
高级查询示例
1. 包含关联数据(Eager Loading)
cs
// 获取书籍及其作者信息
var booksWithAuthors = await _context.Books
.Include(b => b.Author) // 加载关联的Author实体
.Include(b => b.Categories) // 加载关联的Categories集合
.ThenInclude(c => c.Books) // 加载Categories中的Books
.Where(b => b.Price > 50)
.ToListAsync();
// 生成复杂的SQL连接查询
2. 投影查询(只选择需要的字段)
cs
// 只选择需要的字段,提高性能
var bookTitles = await _context.Books
.Where(b => b.IsAvailable)
.Select(b => new
{
b.Id,
b.Title,
AuthorName = b.Author.Name,
Price = b.Price * 0.9m // 计算折扣价
})
.ToListAsync();
// 生成的SQL只选择指定字段
// SELECT b.Id, b.Title, a.Name as AuthorName, b.Price * 0.9 as Price
// FROM Books b JOIN Authors a ON b.AuthorId = a.Id
// WHERE b.IsAvailable = 1
3. 原生SQL查询(当LINQ不够用时)
cs
// 1. 返回实体类型
var books = await _context.Books
.FromSqlRaw("SELECT * FROM Books WHERE Price > {0} AND Author LIKE {1}",
50, "%Smith%")
.ToListAsync();
// 2. 返回非实体类型(DTO)
var bookStats = await _context.Database
.SqlQueryRaw<BookStats>(
"SELECT Author, COUNT(*) as BookCount, AVG(Price) as AvgPrice " +
"FROM Books GROUP BY Author")
.ToListAsync();
// 3. 执行存储过程
var result = await _context.Database
.ExecuteSqlRawAsync("EXEC UpdateBookPrice @BookId, @NewPrice",
bookIdParam, priceParam);
LINQ 与 SQL 语法对比表
最佳实践
1. 性能优化
cs
// ❌ 不好:多次数据库查询
var books = await _context.Books.ToListAsync();
foreach (var book in books)
{
// 每次循环都查询数据库!
var author = await _context.Authors.FindAsync(book.AuthorId);
}
// ✅ 好:一次查询加载所有关联数据
var books = await _context.Books
.Include(b => b.Author) // 一次连接查询
.ToListAsync();
2. 避免N+1查询问题
cs
// ❌ N+1问题:1次查询书籍 + N次查询作者
var books = await _context.Books.ToListAsync();
foreach (var book in books)
{
Console.WriteLine($"{book.Title} by {book.Author.Name}");
}
// ✅ 使用Include解决
var books = await _context.Books
.Include(b => b.Author)
.ToListAsync();
3. 使用异步方法
cs
// ✅ 使用异步版本
await _context.Books.ToListAsync(); // 异步
await _context.SaveChangesAsync(); // 异步
// ❌ 避免同步版本(可能阻塞线程)
_context.Books.ToList(); // 同步
_context.SaveChanges(); // 同步
EF Core 和 Java JPA/Hibernate 对比

总结
-
EF Core使用LINQ编写查询,而不是原始SQL
-
LINQ是延迟执行的,只有在需要结果时才生成SQL
-
SaveChanges()将内存中的更改批量提交到数据库 -
DbContext跟踪实体状态,知道哪些需要增删改
-
Include/ThenInclude 用于加载关联数据
-
投影查询 (
Select) 可以提高性能
核心优势:
-
编译时类型检查
-
IDE智能提示
-
可维护性更好
-
数据库无关性(可切换数据库提供程序)
