检查 .NET 中的分页:使用和不使用实体框架
在现代应用程序中,分页是一项不可或缺的功能,尤其是在处理大数据集时,通过分页将大数据集分割成小块,便于用户逐步获取和显示。这不仅能提升应用性能,还能改善用户体验。本文将详细介绍如何在C#中使用Entity Framework (EF)和ADO.NET实现分页功能,并提供详细的代码注释来帮助理解。
一. 分页的概念
分页指的是将一个大数据集分成较小的部分(即“页面”),并允许用户逐步查看每一页的数据。这在处理数百万条记录时尤为重要,通过分页可以避免一次性加载所有数据,从而提升应用的响应速度。
二. 使用Entity Framework实现分页
1. 项目初始化
首先,假设你已经有一个基于.NET和Entity Framework的项目。在实现分页之前,我们需要先定义一个简单的数据模型和数据库上下文类。
(1) 创建产品模型 Product.cs
public class Product
{
public int Id { get; set; } // 产品ID,主键
public string Name { get; set; } // 产品名称
public decimal Price { get; set; } // 产品价格
}
注释: Product
类定义了一个简单的产品模型,包括Id
、Name
和Price
三个属性,分别表示产品的唯一标识、名称和价格。
(2) 配置数据库上下文 ApplicationDbContext.cs
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : DbContext
{
public DbSet<Product> Products { get; set; } // 定义产品的DbSet集合
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
注释: ApplicationDbContext
类继承自DbContext
,并通过Products
属性定义了对Product
表的访问。
2. 实现分页逻辑
使用Entity Framework中的Skip
和Take
方法可以轻松实现分页。下面展示如何在服务层中编写分页功能。
(1) 产品服务 ProductService.cs
public class ProductService
{
private readonly ApplicationDbContext _context;
public ProductService(ApplicationDbContext context)
{
_context = context; // 通过依赖注入获取数据库上下文
}
public async Task<List<Product>> GetPaginatedProducts(int pageNumber, int pageSize)
{
// 使用Skip和Take方法实现分页
return await _context.Products
.Skip((pageNumber - 1) * pageSize) // 跳过前面页的数据
.Take(pageSize) // 取出当前页的数据
.ToListAsync(); // 转换为异步列表
}
}
注释: GetPaginatedProducts
方法接收页码和每页显示的记录数,并利用Skip
和Take
方法实现分页查询。Skip
负责跳过前面的记录,Take
则用于获取当前页的数据。
3. 实现控制器 ProductsController.cs
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly ProductService _productService;
public ProductsController(ProductService productService)
{
_productService = productService; // 通过依赖注入获取产品服务
}
[HttpGet]
public async Task<IActionResult> Get(int pageNumber = 1, int pageSize = 10)
{
// 调用服务获取分页数据
var products = await _productService.GetPaginatedProducts(pageNumber, pageSize);
return Ok(products); // 返回结果
}
}
注释: ProductsController
类通过REST API的形式提供分页查询功能,客户端可以通过指定pageNumber
和pageSize
获取不同页面的数据。
4. 配置依赖注入 Program.cs
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// 添加控制器服务
builder.Services.AddControllers();
// 配置Entity Framework的数据库连接
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// 注册ProductService依赖注入
builder.Services.AddScoped<ProductService>();
var app = builder.Build();
// 配置HTTP请求管道
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers(); // 映射控制器
});
app.Run(); // 启动应用程序
注释: 在Program.cs
中,我们配置了Entity Framework的数据库上下文,并注册了产品服务ProductService
以便通过依赖注入使用。
三. 不使用Entity Framework的分页实现
如果不使用Entity Framework,也可以通过ADO.NET直接执行SQL查询实现分页。下面展示如何使用存储过程和ADO.NET实现分页。
1. 设置数据访问层
(1) 产品模型 Product.cs
public class Product
{
public int Id { get; set; } // 产品ID
public string Name { get; set; } // 产品名称
public decimal Price { get; set; } // 产品价格
}
注释: 与Entity Framework的实现类似,我们定义一个产品模型用于存储从数据库中获取的数据。
(2) 产品仓储类 ProductRepository.cs
using System.Data;
using System.Data.SqlClient;
public class ProductRepository
{
private readonly string _connectionString;
public ProductRepository(string connectionString)
{
_connectionString = connectionString; // 初始化数据库连接字符串
}
public List<Product> GetPaginatedProducts(int pageNumber, int pageSize)
{
var products = new List<Product>(); // 初始化产品列表
using (var connection = new SqlConnection(_connectionString))
{
var command = new SqlCommand("sp_GetPaginatedProducts", connection) // 使用存储过程
{
CommandType = CommandType.StoredProcedure
};
command.Parameters.AddWithValue("@PageNumber", pageNumber); // 设置页码参数
command.Parameters.AddWithValue("@PageSize", pageSize); // 设置每页记录数参数
connection.Open(); // 打开数据库连接
using (var reader = command.ExecuteReader()) // 执行查询
{
while (reader.Read()) // 读取每一条记录
{
products.Add(new Product
{
Id = Convert.ToInt32(reader["Id"]), // 获取产品ID
Name = reader["Name"].ToString(), // 获取产品名称
Price = Convert.ToDecimal(reader["Price"]) // 获取产品价格
});
}
}
}
return products; // 返回分页后的产品列表
}
}
注释: ProductRepository
通过ADO.NET执行SQL存储过程实现分页查询,GetPaginatedProducts
方法直接从数据库中获取分页数据。
2. SQL存储过程 sp_GetPaginatedProducts
CREATE PROCEDURE sp_GetPaginatedProducts
@PageNumber INT,
@PageSize INT
AS
BEGIN
SET NOCOUNT ON;
-- 使用OFFSET和FETCH实现分页
SELECT Id, Name, Price
FROM Products
ORDER BY Id
OFFSET (@PageNumber - 1) * @PageSize ROWS
FETCH NEXT @PageSize ROWS ONLY;
END
注释: 该存储过程使用OFFSET
和FETCH
子句来实现分页,分页逻辑在数据库端执行,提高了性能。
3. 实现服务和控制器
(1) 产品服务 ProductService.cs
public class ProductService
{
private readonly ProductRepository _productRepository;
public ProductService(ProductRepository productRepository)
{
_productRepository = productRepository; // 通过依赖注入获取产品仓储类
}
public List<Product> GetPaginatedProducts(int pageNumber, int pageSize)
{
// 调用产品仓储类的方法获取分页数据
return _productRepository.GetPaginatedProducts(pageNumber, pageSize);
}
}
注释: 该服务类通过依赖注入调用ProductRepository
,并提供分页功能给控制器使用。
(2) 产品控制器 ProductsController.cs
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly ProductService _productService;
public ProductsController(ProductService productService)
{
_productService = productService; // 通过依赖注入获取产品服务
}
[HttpGet]
public IActionResult Get(int pageNumber = 1, int pageSize = 10)
{
// 调用服务获取分页数据
var products = _productService.GetPaginatedProducts(pageNumber, pageSize);
return Ok(products); // 返回结果
}
}
注释: 该控制器通过服务提供分页API接口,允许客户端通过查询参数pageNumber
和pageSize
获取数据。
4. 配置依赖注入
Program.cs
var builder = WebApplication.CreateBuilder(args);
// 添加控制器服务
builder.Services.AddControllers();
// 注册ProductRepository依赖注入
builder.Services.AddScoped<ProductRepository>(provider =>
new ProductRepository(builder.Configuration.GetConnectionString("DefaultConnection")));
// 注册ProductService依赖注入
builder.Services.AddScoped<ProductService>();
var app = builder.Build();
// 配置HTTP请求管道
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers(); // 映射控制器
});
app.Run(); // 启动应用程序
注释: 在Program.cs
中配置依赖注入,确保ProductRepository
和ProductService
能够在整个应用程序中使用。
四. 总结
通过本文,我们了解了如何在C#项目中使用Entity Framework和ADO.NET实现分页功能。Entity Framework提供了简便的Skip
和Take
方法来实现分页,而ADO.NET则通过存储过程直接在数据库端实现分页。根据项目的需求,开发者可以选择合适的实现方式。