405 lines
13 KiB
C#
405 lines
13 KiB
C#
using Common.ExpandFun;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Sharp.Data;
|
|
using Sharp.Data.Context;
|
|
|
|
namespace Data.Test;
|
|
|
|
/// <summary>
|
|
/// 演示 05: 分页查询
|
|
///
|
|
/// 本测试类演示:
|
|
/// 1. 基础分页查询
|
|
/// 2. 带条件的分页
|
|
/// 3. 带排序的分页
|
|
/// 4. 分页结果的使用
|
|
/// 5. 复用 Common 模块的 PagedResult
|
|
/// </summary>
|
|
[TestClass]
|
|
public class _05_PaginationTests
|
|
{
|
|
private IDataContext _db = null!;
|
|
|
|
[TestInitialize]
|
|
public async Task Setup()
|
|
{
|
|
var builder = new DataContextBuilder<TestDbContext>();
|
|
builder.UseInMemoryDatabase($"PaginationDemo_{Guid.NewGuid()}");
|
|
_db = builder.Build();
|
|
|
|
// 添加测试数据:100 个产品
|
|
var repo = _db.Repository<Product>();
|
|
var products = new List<Product>();
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 演示: 基础分页查询
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task Demo_BasicPagination()
|
|
{
|
|
var repo = _db.Repository<Product>();
|
|
|
|
// 查询第 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}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 演示: 遍历所有页
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task Demo_IterateAllPages()
|
|
{
|
|
var repo = _db.Repository<Product>();
|
|
var pageSize = 15;
|
|
var currentPage = 1;
|
|
|
|
Console.WriteLine($"=== 遍历所有页(每页 {pageSize} 条)===\n");
|
|
|
|
PagedResult<Product> 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 页
|
|
}
|
|
|
|
/// <summary>
|
|
/// 演示: 带条件的分页查询
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task Demo_PaginationWithFilter()
|
|
{
|
|
var repo = _db.Repository<Product>();
|
|
|
|
// 查询价格 > 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 演示: 带排序的分页查询
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task Demo_PaginationWithOrdering()
|
|
{
|
|
var repo = _db.Repository<Product>();
|
|
|
|
// 按价格降序排列
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 演示: 复杂分页查询 - 组合条件、排序、分页
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task Demo_ComplexPagination()
|
|
{
|
|
var repo = _db.Repository<Product>();
|
|
|
|
// 查询热门产品,价格 > 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 演示: 分页辅助信息的使用
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task Demo_PaginationHelpers()
|
|
{
|
|
var repo = _db.Repository<Product>();
|
|
|
|
// 第 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 演示: 空结果的分页
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task Demo_EmptyPageResult()
|
|
{
|
|
var repo = _db.Repository<Product>();
|
|
|
|
// 查询不存在的条件
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 演示: 超出范围的页码
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task Demo_OutOfRangePage()
|
|
{
|
|
var repo = _db.Repository<Product>();
|
|
|
|
// 请求第 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 演示: 在 UI 中使用分页
|
|
///
|
|
/// 模拟 Web API 或 WPF 应用的分页场景
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task Demo_PaginationInUI()
|
|
{
|
|
var repo = _db.Repository<Product>();
|
|
|
|
// 模拟用户请求
|
|
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 返回给前端");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 演示: PagedResult 来自 Common 模块
|
|
///
|
|
/// Sharp.Data 复用 Common.ExpandFun.PagedResult<T>,避免重复定义
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task Demo_PagedResultFromCommonModule()
|
|
{
|
|
var repo = _db.Repository<Product>();
|
|
|
|
var result = await repo.GetPagedAsync(1, 10);
|
|
|
|
// PagedResult<T> 来自 Common.ExpandFun 命名空间
|
|
Console.WriteLine($"✓ PagedResult<T> 的类型信息:");
|
|
Console.WriteLine($" 类型: {result.GetType().FullName}");
|
|
Console.WriteLine($" 命名空间: {result.GetType().Namespace}");
|
|
Console.WriteLine($"\n✓ 复用 Common 模块,避免重复造轮子");
|
|
|
|
Assert.AreEqual("Common.ExpandFun", result.GetType().Namespace);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 演示: 自定义扩展方法进行分页
|
|
/// </summary>
|
|
[TestMethod]
|
|
public async Task Demo_CustomPaginationExtension()
|
|
{
|
|
// 使用 EF Core 的 IQueryable 直接分页
|
|
var query = _db.Context.Set<Product>()
|
|
.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<Product>
|
|
{
|
|
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 进行自定义分页");
|
|
}
|
|
} |