using Common.ExpandFun; using Microsoft.EntityFrameworkCore; using Sharp.Data; using Sharp.Data.Context; namespace Data.Test; /// /// 演示 05: 分页查询 /// /// 本测试类演示: /// 1. 基础分页查询 /// 2. 带条件的分页 /// 3. 带排序的分页 /// 4. 分页结果的使用 /// 5. 复用 Common 模块的 PagedResult /// [TestClass] public class _05_PaginationTests { private IDataContext _db = null!; [TestInitialize] public async Task Setup() { var builder = new DataContextBuilder(); builder.UseInMemoryDatabase($"PaginationDemo_{Guid.NewGuid()}"); _db = builder.Build(); // 添加测试数据:100 个产品 var repo = _db.Repository(); var products = new List(); for (var i = 1; i <= 100; i++) products.Add(new Product { Name = $"产品{i:D3}", Price = (i % 10 + 1) * 100m, Stock = i * 5, Description = i % 2 == 0 ? "热门产品" : "普通产品" }); await repo.AddRangeAsync(products); await _db.SaveChangesAsync(); Console.WriteLine("✓ 初始化完成:创建了 100 个测试产品\n"); } [TestCleanup] public void Cleanup() { _db?.Dispose(); } /// /// 演示: 基础分页查询 /// [TestMethod] public async Task Demo_BasicPagination() { var repo = _db.Repository(); // 查询第 1 页,每页 10 条 var page1 = await repo.GetPagedAsync( 1, 10); Console.WriteLine($"第 1 页:"); Console.WriteLine($" 当前页: {page1.PageIndex}"); Console.WriteLine($" 每页数量: {page1.PageSize}"); Console.WriteLine($" 总记录数: {page1.TotalCount}"); Console.WriteLine($" 总页数: {page1.TotalPages}"); Console.WriteLine($" 是否有上一页: {page1.HasPreviousPage}"); Console.WriteLine($" 是否有下一页: {page1.HasNextPage}"); // 验证 Assert.AreEqual(1, page1.PageIndex); Assert.AreEqual(10, page1.PageSize); Assert.AreEqual(100, page1.TotalCount); Assert.AreEqual(10, page1.TotalPages); Assert.AreEqual(10, page1.Items.Count); Assert.IsFalse(page1.HasPreviousPage); Assert.IsTrue(page1.HasNextPage); Console.WriteLine($"\n产品列表:"); foreach (var product in page1.Items) Console.WriteLine($" - {product.Name}: {product.Price:C}"); } /// /// 演示: 遍历所有页 /// [TestMethod] public async Task Demo_IterateAllPages() { var repo = _db.Repository(); var pageSize = 15; var currentPage = 1; Console.WriteLine($"=== 遍历所有页(每页 {pageSize} 条)===\n"); PagedResult pageResult; do { pageResult = await repo.GetPagedAsync(currentPage, pageSize); Console.WriteLine($"第 {currentPage} 页: {pageResult.Items.Count} 条记录"); Console.WriteLine( $" 范围: {(currentPage - 1) * pageSize + 1} - {Math.Min(currentPage * pageSize, pageResult.TotalCount)}"); // 处理当前页数据 foreach (var product in pageResult.Items) { // 业务逻辑... } currentPage++; } while (pageResult.HasNextPage); Console.WriteLine($"\n✓ 总共遍历了 {currentPage - 1} 页"); Assert.AreEqual(7, currentPage - 1); // 100 / 15 = 7 页 } /// /// 演示: 带条件的分页查询 /// [TestMethod] public async Task Demo_PaginationWithFilter() { var repo = _db.Repository(); // 查询价格 > 500 的产品,第 1 页,每页 10 条 var result = await repo.GetPagedAsync( 1, 10, p => p.Price > 500m); Console.WriteLine($"查询条件: 价格 > 500"); Console.WriteLine($" 总记录数: {result.TotalCount}"); Console.WriteLine($" 总页数: {result.TotalPages}"); Console.WriteLine($" 当前页记录数: {result.Items.Count}"); Console.WriteLine($"\n产品列表:"); foreach (var product in result.Items) { Console.WriteLine($" - {product.Name}: {product.Price:C}"); Assert.IsTrue(product.Price > 500m); } } /// /// 演示: 带排序的分页查询 /// [TestMethod] public async Task Demo_PaginationWithOrdering() { var repo = _db.Repository(); // 按价格降序排列 var result = await repo.GetPagedAsync( 1, 10, null, q => q.OrderByDescending(p => p.Price)); Console.WriteLine($"排序: 按价格降序"); Console.WriteLine($"\n前 10 个最贵的产品:"); foreach (var product in result.Items) Console.WriteLine($" - {product.Name}: {product.Price:C}"); // 验证排序 for (var i = 0; i < result.Items.Count - 1; i++) Assert.IsTrue(result.Items[i].Price >= result.Items[i + 1].Price); } /// /// 演示: 复杂分页查询 - 组合条件、排序、分页 /// [TestMethod] public async Task Demo_ComplexPagination() { var repo = _db.Repository(); // 查询热门产品,价格 > 300,按库存降序,第 2 页,每页 5 条 var result = await repo.GetPagedAsync( 2, 5, p => p.Description == "热门产品" && p.Price > 300m, q => q.OrderByDescending(p => p.Stock)); Console.WriteLine($"复杂查询:"); Console.WriteLine($" 条件: 热门产品 AND 价格>300"); Console.WriteLine($" 排序: 按库存降序"); Console.WriteLine($" 分页: 第 2 页,每页 5 条"); Console.WriteLine($"\n结果:"); Console.WriteLine($" 总记录数: {result.TotalCount}"); Console.WriteLine($" 总页数: {result.TotalPages}"); Console.WriteLine($" 当前页码: {result.PageIndex}"); Console.WriteLine($"\n产品列表:"); foreach (var product in result.Items) { Console.WriteLine($" - {product.Name}: {product.Price:C}, 库存: {product.Stock}"); Assert.AreEqual("热门产品", product.Description); Assert.IsTrue(product.Price > 300m); } } /// /// 演示: 分页辅助信息的使用 /// [TestMethod] public async Task Demo_PaginationHelpers() { var repo = _db.Repository(); // 第 5 页,每页 10 条 var result = await repo.GetPagedAsync(5, 10); Console.WriteLine($"=== 分页辅助信息 ===\n"); Console.WriteLine($"当前页: {result.PageIndex}"); Console.WriteLine($"每页数量: {result.PageSize}"); Console.WriteLine($"总记录数: {result.TotalCount}"); Console.WriteLine($"总页数: {result.TotalPages}"); Console.WriteLine($"是否第一页: {result.PageIndex == 1}"); Console.WriteLine($"是否最后一页: {result.PageIndex == result.TotalPages}"); Console.WriteLine($"有上一页: {result.HasPreviousPage}"); Console.WriteLine($"有下一页: {result.HasNextPage}"); // 计算记录范围 var startIndex = (result.PageIndex - 1) * result.PageSize + 1; var endIndex = Math.Min(result.PageIndex * result.PageSize, result.TotalCount); Console.WriteLine($"\n记录范围: {startIndex} - {endIndex}"); // 验证 Assert.AreEqual(5, result.PageIndex); Assert.AreEqual(41, startIndex); Assert.AreEqual(50, endIndex); Assert.IsTrue(result.HasPreviousPage); Assert.IsTrue(result.HasNextPage); } /// /// 演示: 空结果的分页 /// [TestMethod] public async Task Demo_EmptyPageResult() { var repo = _db.Repository(); // 查询不存在的条件 var result = await repo.GetPagedAsync( 1, 10, p => p.Price > 99999m); Console.WriteLine($"查询不存在的记录:"); Console.WriteLine($" 总记录数: {result.TotalCount}"); Console.WriteLine($" 总页数: {result.TotalPages}"); Console.WriteLine($" 当前页记录数: {result.Items.Count}"); Assert.AreEqual(0, result.TotalCount); Assert.AreEqual(0, result.TotalPages); Assert.AreEqual(0, result.Items.Count); Assert.IsFalse(result.HasPreviousPage); Assert.IsFalse(result.HasNextPage); } /// /// 演示: 超出范围的页码 /// [TestMethod] public async Task Demo_OutOfRangePage() { var repo = _db.Repository(); // 请求第 999 页(超出实际页数) var result = await repo.GetPagedAsync(999, 10); Console.WriteLine($"请求超出范围的页码 (第 999 页):"); Console.WriteLine($" 总记录数: {result.TotalCount}"); Console.WriteLine($" 总页数: {result.TotalPages}"); Console.WriteLine($" 当前页码: {result.PageIndex}"); Console.WriteLine($" 当前页记录数: {result.Items.Count}"); // 超出范围返回空列表 Assert.AreEqual(0, result.Items.Count); Assert.AreEqual(999, result.PageIndex); Assert.IsFalse(result.HasNextPage); } /// /// 演示: 在 UI 中使用分页 /// /// 模拟 Web API 或 WPF 应用的分页场景 /// [TestMethod] public async Task Demo_PaginationInUI() { var repo = _db.Repository(); // 模拟用户请求 var requestedPage = 3; var pageSize = 20; var searchTerm = "产品0"; // 用户搜索 Console.WriteLine($"=== UI 分页场景 ==="); Console.WriteLine($"用户请求: 第 {requestedPage} 页, 每页 {pageSize} 条"); Console.WriteLine($"搜索词: {searchTerm}\n"); // 执行分页查询 var result = await repo.GetPagedAsync( requestedPage, pageSize, string.IsNullOrEmpty(searchTerm) ? null : p => p.Name.Contains(searchTerm), q => q.OrderBy(p => p.Name)); // 构建返回给前端的数据 var uiData = new { Success = true, Data = new { Items = result.Items.Select(p => new { p.Id, p.Name, p.Price, p.Stock }).ToList(), Pagination = new { result.PageIndex, result.PageSize, result.TotalCount, result.TotalPages, result.HasPreviousPage, result.HasNextPage } } }; Console.WriteLine($"返回数据:"); Console.WriteLine($" 记录数: {uiData.Data.Items.Count}"); Console.WriteLine($" 分页信息: {uiData.Data.Pagination.PageIndex}/{uiData.Data.Pagination.TotalPages}"); Console.WriteLine($"\n✓ 可以直接序列化为 JSON 返回给前端"); } /// /// 演示: PagedResult 来自 Common 模块 /// /// Sharp.Data 复用 Common.ExpandFun.PagedResult,避免重复定义 /// [TestMethod] public async Task Demo_PagedResultFromCommonModule() { var repo = _db.Repository(); var result = await repo.GetPagedAsync(1, 10); // PagedResult 来自 Common.ExpandFun 命名空间 Console.WriteLine($"✓ PagedResult 的类型信息:"); Console.WriteLine($" 类型: {result.GetType().FullName}"); Console.WriteLine($" 命名空间: {result.GetType().Namespace}"); Console.WriteLine($"\n✓ 复用 Common 模块,避免重复造轮子"); Assert.AreEqual("Common.ExpandFun", result.GetType().Namespace); } /// /// 演示: 自定义扩展方法进行分页 /// [TestMethod] public async Task Demo_CustomPaginationExtension() { // 使用 EF Core 的 IQueryable 直接分页 var query = _db.Context.Set() .Where(p => p.Price > 500m) .OrderBy(p => p.Name); // 手动实现分页逻辑 var page = 2; var pageSize = 10; var totalCount = await query.CountAsync(); var items = await query .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); // 构建 PagedResult var result = new PagedResult { Items = items, TotalCount = totalCount, PageIndex = page, PageSize = pageSize }; Console.WriteLine($"自定义分页:"); Console.WriteLine($" 总记录数: {result.TotalCount}"); Console.WriteLine($" 当前页: {result.PageIndex}"); Console.WriteLine($" 记录数: {result.Items.Count}"); Console.WriteLine($"\n✓ 可以直接使用 IQueryable 和 PagedResult 进行自定义分页"); } }