检查 .NET 中的分页:使用和不使用实体框架

发布:2024-10-17 15:36 阅读:112 点赞:0

在现代应用程序中,分页是一项不可或缺的功能,尤其是在处理大数据集时,通过分页将大数据集分割成小块,便于用户逐步获取和显示。这不仅能提升应用性能,还能改善用户体验。本文将详细介绍如何在C#中使用Entity Framework (EF)和ADO.NET实现分页功能,并提供详细的代码注释来帮助理解。

一. 分页的概念

分页指的是将一个大数据集分成较小的部分(即“页面”),并允许用户逐步查看每一页的数据。这在处理数百万条记录时尤为重要,通过分页可以避免一次性加载所有数据,从而提升应用的响应速度。

二. 使用Entity Framework实现分页

1. 项目初始化

首先,假设你已经有一个基于.NET和Entity Framework的项目。在实现分页之前,我们需要先定义一个简单的数据模型和数据库上下文类。

(1) 创建产品模型 Product.cs

public class Product
{
    public int Id { getset; } // 产品ID,主键
    public string Name { getset; } // 产品名称
    public decimal Price { getset; } // 产品价格
}

注释: Product类定义了一个简单的产品模型,包括IdNamePrice三个属性,分别表示产品的唯一标识、名称和价格。

(2) 配置数据库上下文 ApplicationDbContext.cs

using Microsoft.EntityFrameworkCore;

public class ApplicationDbContext : DbContext
{
    public DbSet<Product> Products { getset; } // 定义产品的DbSet集合

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)

    {
    }
}

注释: ApplicationDbContext类继承自DbContext,并通过Products属性定义了对Product表的访问。

2. 实现分页逻辑

使用Entity Framework中的SkipTake方法可以轻松实现分页。下面展示如何在服务层中编写分页功能。

(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方法接收页码和每页显示的记录数,并利用SkipTake方法实现分页查询。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 = 1int pageSize = 10)
    {
        // 调用服务获取分页数据
        var products = await _productService.GetPaginatedProducts(pageNumber, pageSize);
        return Ok(products); // 返回结果
    }
}

注释: ProductsController类通过REST API的形式提供分页查询功能,客户端可以通过指定pageNumberpageSize获取不同页面的数据。

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 { getset; } // 产品ID
    public string Name { getset; } // 产品名称
    public decimal Price { getset; } // 产品价格
}

注释: 与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 IdName, Price
    FROM Products
    ORDER BY Id
    OFFSET (@PageNumber - 1) * @PageSize ROWS
    FETCH NEXT @PageSize ROWS ONLY;
END

注释: 该存储过程使用OFFSETFETCH子句来实现分页,分页逻辑在数据库端执行,提高了性能。

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 = 1int pageSize = 10)
    {
        // 调用服务获取分页数据
        var products = _productService.GetPaginatedProducts(pageNumber, pageSize);
        return Ok(products); // 返回结果
    }
}

注释: 该控制器通过服务提供分页API接口,允许客户端通过查询参数pageNumberpageSize获取数据。

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中配置依赖注入,确保ProductRepositoryProductService能够在整个应用程序中使用。

四. 总结

通过本文,我们了解了如何在C#项目中使用Entity Framework和ADO.NET实现分页功能。Entity Framework提供了简便的SkipTake方法来实现分页,而ADO.NET则通过存储过程直接在数据库端实现分页。根据项目的需求,开发者可以选择合适的实现方式。