如何用C#编写API实现分页排序过滤,避免返回全量数据至前端
最优实现方案:C# API + KendoReact 网格 + Redux 大数据量处理
一、后端C# API 实现(分页+过滤+排序)
核心思路是让数据库层面完成分页、过滤、排序操作,避免全量数据加载。用EF Core的延迟查询特性,将前端参数转化为SQL语句执行。
1. 定义请求参数DTO
public class GridRequestDto { public int PageIndex { get; set; } = 1; public int PageSize { get; set; } = 20; public string? Filter { get; set; } public string? SortField { get; set; } public string? SortDirection { get; set; } = "asc"; }
2. 控制器核心逻辑
[HttpPost("grid-data")] public async Task<IActionResult> GetGridData([FromBody] GridRequestDto request) { var query = _dbContext.TargetTable.AsQueryable(); // 过滤逻辑(示例:多字段模糊匹配,可根据业务扩展) if (!string.IsNullOrEmpty(request.Filter)) { var filterValue = request.Filter.ToLower(); query = query.Where(x => x.Field1.ToLower().Contains(filterValue) || x.Field2.ToLower().Contains(filterValue)); } // 排序逻辑(限制合法排序字段,防止SQL注入) var allowedSortFields = new List<string> { "Field1", "Field2", "Field3" }; if (!string.IsNullOrEmpty(request.SortField) && allowedSortFields.Contains(request.SortField)) { query = request.SortDirection?.ToLower() == "desc" ? query.OrderByDescending(x => EF.Property<object>(x, request.SortField)) : query.OrderBy(x => EF.Property<object>(x, request.SortField)); } // 分页查询:先统计总条数,再取当前页数据 var totalCount = await query.CountAsync(); var pageData = await query .Skip((request.PageIndex - 1) * request.PageSize) .Take(request.PageSize) .ToListAsync(); return Ok(new { Total = totalCount, Data = pageData }); }
二、前端KendoReact 网格配置
利用KendoGrid内置的分页、过滤、排序事件,将状态变化同步到后端API,仅加载当前页数据。
核心组件实现
import { Grid, GridColumn } from '@progress/kendo-react-grid'; import { useSelector, useDispatch } from 'react-redux'; import { setGridData, setTotalCount, setGridLoading } from '../redux/gridSlice'; const DataGrid = () => { const { gridData, totalCount, isLoading } = useSelector(state => state.grid); const dispatch = useDispatch(); // 统一处理分页/过滤/排序事件 const handleGridStateChange = async (event) => { const { page, filter, sort } = event; // 构造后端请求参数 const requestParams = { PageIndex: page.current + 1, // Kendo分页从0开始,后端从1开始 PageSize: page.size, Filter: filter?.filters?.[0]?.value || '', SortField: sort?.[0]?.field || '', SortDirection: sort?.[0]?.dir || 'asc' }; dispatch(setGridLoading(true)); try { const response = await fetch('/api/yourcontroller/grid-data', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(requestParams) }); const result = await response.json(); dispatch(setGridData(result.Data)); dispatch(setTotalCount(result.Total)); } catch (err) { console.error('数据加载失败:', err); } finally { dispatch(setGridLoading(false)); } }; return ( <Grid data={gridData} total={totalCount} pageable={{ pageSizes: [10, 20, 50] }} filterable={true} sortable={true} onPageChange={handleGridStateChange} onFilterChange={handleGridStateChange} onSortChange={handleGridStateChange} loading={isLoading} > <GridColumn field="Field1" title="字段1" /> <GridColumn field="Field2" title="字段2" /> {/* 其他列配置 */} </Grid> ); }; export default DataGrid;
三、Redux 状态管理优化
针对大数据场景,仅存储必要状态,避免冗余数据占用内存:
1. 初始状态设计
const initialState = { gridData: [], // 当前页数据 totalCount: 0, // 总条数(用于分页计算) isLoading: false, // 加载状态 currentParams: { // 记录当前请求参数,避免重复请求 PageIndex: 1, PageSize: 20, Filter: '', SortField: '', SortDirection: 'asc' } };
2. 关键优化点
- 在
handleGridStateChange中对比新参数与currentParams,参数无变化时跳过请求 - 可选:添加缓存逻辑,对已请求过的页面数据进行缓存(需注意数据更新时清空缓存)
四、关键注意事项
- 后端必须使用
AsQueryable延迟查询,禁止先调用ToList()再在内存中处理数据 - 排序字段需做白名单校验,防止SQL注入风险
- 前端KendoGrid必须配置
total属性,确保分页组件正常显示总页数 - Redux仅存储当前页数据和总条数,不要保存全量数据
内容的提问来源于stack exchange,提问作者Baba
相关产品推荐
相关产品推荐

