.Net Core 学习:Razor Pages -- EF Core工作原理

核心概念: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 对比


总结

  1. EF Core使用LINQ编写查询,而不是原始SQL

  2. LINQ是延迟执行的,只有在需要结果时才生成SQL

  3. SaveChanges() 将内存中的更改批量提交到数据库

  4. DbContext跟踪实体状态,知道哪些需要增删改

  5. Include/ThenInclude 用于加载关联数据

  6. 投影查询 (Select) 可以提高性能

核心优势

  • 编译时类型检查

  • IDE智能提示

  • 可维护性更好

  • 数据库无关性(可切换数据库提供程序)

相关推荐
北岛寒沫1 小时前
北京大学国家发展研究院 经济学辅修 经济学原理课程笔记(第三课 需求与供应弹性)
数据库·人工智能·笔记
不想画图1 小时前
数据库基础操作和权限管理
数据库·mysql
如果未来,1 小时前
Oracle数据库的存储结构以及表空间的概念
数据库·oracle
清涧游1 小时前
第九章-NOP团队dmz-A
笔记·学习·安全
sensen_kiss1 小时前
INT303 Big Data Analysis 大数据分析 Pt.10 分析模型和混合模型
大数据·学习·机器学习·数据挖掘·数据分析
QiZhang | UESTC1 小时前
学习日记day41
学习
光头程序员1 小时前
学习笔记——常识解答之垃圾回收机制
java·笔记·学习
b***46241 小时前
Redis开启远程访问
数据库·redis·缓存
KaiwuDB1 小时前
KaiwuDB X 向明智控:基于 KaiwuDB 的煤矿综采数据分析系统
数据库