.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智能提示

  • 可维护性更好

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

相关推荐
Re.不晚10 分钟前
MySQL进阶之战——索引、事务与锁、高可用架构的三重奏
数据库·mysql·架构
wdfk_prog14 分钟前
[Linux]学习笔记系列 -- [drivers][I2C]I2C
linux·笔记·学习
老邓计算机毕设19 分钟前
SSM智慧社区信息化服务平台4v5hv(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架·智慧社区、·信息化平台
盐焗西兰花24 分钟前
鸿蒙学习实战之路-Reader Kit自定义字体最佳实践
学习·华为·harmonyos
麦聪聊数据40 分钟前
为何通用堡垒机无法在数据库运维中实现精准风控?
数据库·sql·安全·低代码·架构
2301_7903009644 分钟前
Python数据库操作:SQLAlchemy ORM指南
jvm·数据库·python
m0_736919101 小时前
用Pandas处理时间序列数据(Time Series)
jvm·数据库·python
亓才孓1 小时前
[JDBC]PreparedStatement替代Statement
java·数据库
近津薪荼1 小时前
dfs专题5——(二叉搜索树中第 K 小的元素)
c++·学习·算法·深度优先
m0_466525291 小时前
绿盟科技风云卫AI安全能力平台成果重磅发布
大数据·数据库·人工智能·安全