
简介本资源是一套基于C#与EasyUI的Web开发实战示例面向具备一定ASP.NET MVC基础、希望快速掌握后台管理常用交互的开发者。项目采用MVC EasyUI SQL Server 2014架构在VS2013下编译涵盖新增、修改、删除、分页、导出Excel文件与上传图片等典型功能适合作为中小型管理系统的起步模板或课程设计参考。压缩包为rar格式整体约28.54MB内含完整的项目源码与数据库脚本主要文件类型包括C#源码、EasyUI前端脚本与样式、视图页面及配置文档便于直接运行与二次改造。示例仅涉及一张表datagrid分页默认使用SQL Server 2012新增的分页关键字若数据库为2005或2008可参考UserInfoDAL.cs中的getPage2005方法调整分页逻辑。目前已有218人学习下载读者可借此理解MVC分层、EasyUI组件调用与Excel导出、文件上传的实现思路快速搭建可用的增删改查后台。1. 从一张后台管理页说起C#EasyUI 的增删改导出上传到底难在哪很多做企业信息系统的开发者都遇到过这种场景一个后台管理模块需求文档上写着“支持新增、修改、删除、导出 Excel、上传附件”看起来就是五个按钮的事但真动手写起来前后端联调、数据格式转换、文件流处理、批量操作的事务一致性每一个都能让人加班到深夜。C# 负责后端逻辑与数据访问EasyUI 负责前端表格与表单渲染这套组合在中小型管理系统中非常常见尤其适合快速交付内部使用的数据维护平台。它的核心价值在于用尽量少的自定义前端代码把 CRUD 和文件交互跑通让开发者把精力放在业务规则而不是 UI 细节上。这篇文章面向的是已经会用 C# 写接口、用 EasyUI 搭过简单页面的从业者我会把每个环节的最小实现、参数配置和踩过的坑讲清楚让你能直接照着复现一套可用的模块。2. 把增删改做成一条流水线后端接口与前端 datagrid 的对接方式2.1 为什么选 EasyUI datagrid 作为数据载体EasyUI 的 datagrid 组件自带分页、排序、行内编辑和工具栏对于管理后台来说它把表格渲染、数据绑定、事件回调都封装好了。你只需要在 HTML 里声明一个 table 标签然后用 jQuery 调用 datagrid 方法传入 url 和 columns 就能拿到数据。后端 C# 这边常见做法是用一般处理程序.ashx或者 ASP.NET MVC 的 Controller 返回 JSON。选型上如果项目已经有 MVC 框架用 Controller 更自然如果是老式 WebForm 项目ashx 更轻量。关键点在于EasyUI datagrid 默认要求返回的 JSON 包含 total 和 rows 两个字段total 是总记录数rows 是当前页的数据数组。很多新手第一次对接时只返回了数组结果分页栏显示不出来这就是没对齐数据契约。2.2 新增与修改用同一个表单弹窗还是分开我一般会用一个弹窗同时处理新增和修改通过隐藏字段区分模式。新增时清空表单修改时回填数据。这样做的好处是减少重复 HTML缺点是表单验证逻辑要写两套分支。具体实现上EasyUI 的 dialog 组件配合 form 组件用$(#dlg).dialog(open)打开用$(#fm).form(load, row)回填。提交时用$(#fm).form(submit, {url: ..., onSubmit: ..., success: ...})。后端接收时新增走 INSERT修改走 UPDATE主键由前端传回。这里有一个容易翻车的地方EasyUI 的 form 提交默认会把所有字段序列化包括那些只读的展示字段如果后端直接拿这些字段去更新可能会把不该改的列覆盖掉。稳妥的做法是后端只取需要的字段或者前端在提交前禁用只读字段。2.3 删除单条删除与批量删除的事务边界删除操作看起来简单但批量删除时如果逐条执行 SQL中间某条失败就会导致部分删除。我通常会在后端开一个事务把选中的 ID 列表拼成IN条件一次性执行DELETE FROM 表 WHERE ID IN (...)。前端这边EasyUI datagrid 的getSelections方法返回选中行数组取其中 id 字段组成逗号分隔的字符串传给后端。注意如果 ID 是字符串类型要防止 SQL 注入用参数化查询或者存储过程。另外删除前一定要给用户确认提示用$.messager.confirm弹窗避免误操作。还有一个细节删除后要刷新 datagrid用$(#dg).datagrid(reload)而不是重新加载整个页面。2.4 一个可复用的后端接口示例下面是一个用 ashx 处理新增和修改的简化代码展示了参数接收和 JSON 返回的基本结构。// Handler.ashx 中处理新增/修改的核心逻辑 public void ProcessRequest(HttpContext context) { context.Response.ContentType application/json; string action context.Request[action]; // 前端传动作标识 string result ; if (action save) { int id Convert.ToInt32(context.Request[id] ?? 0); string name context.Request[name]; string remark context.Request[remark]; // 参数化查询防止注入 string sql id 0 ? UPDATE T_Item SET NameName, RemarkRemark WHERE IDID : INSERT INTO T_Item (Name, Remark) VALUES (Name, Remark); SqlParameter[] paras { new SqlParameter(Name, name), new SqlParameter(Remark, remark), new SqlParameter(ID, id) }; int rows DbHelper.ExecuteNonQuery(sql, paras); result rows 0 ? {\success\:true} : {\success\:false,\msg\:\操作失败\}; } context.Response.Write(result); }这段代码里action是前端区分操作的标识id为 0 表示新增大于 0 表示修改。DbHelper是封装好的数据库工具类实际项目中可以用 Dapper 或 EF 替代。返回的 JSON 结构要和前端 success 回调里解析的字段一致否则前端会误判。参数化查询是必须的直接拼字符串在批量操作时风险极高。3. 导出 Excel 与上传文件流处理与格式兼容的实战细节3.1 导出 Excel 的两种路线NPOI 与 EPPlus 怎么选C# 导出 Excel 最常用的两个库是 NPOI 和 EPPlus。NPOI 完全免费支持 .xls 和 .xlsx 两种格式适合对成本敏感的项目EPPlus 从 5.0 开始商业授权但 API 更简洁适合新项目且预算允许的情况。我一般用 NPOI因为老项目兼容性好而且社区资料多。导出时后端生成 Excel 文件流设置响应头Content-Disposition为 attachment文件名用 URL 编码防止中文乱码。前端这边EasyUI 的 datagrid 可以直接把当前页数据传给后端也可以只传查询条件让后端导出全部。如果数据量大建议后端分页查询后分批写入 Excel避免内存溢出。3.2 用 NPOI 生成 Excel 并输出到浏览器的完整步骤下面是一个导出 Excel 的 ashx 示例展示了工作簿创建、表头写入和流输出的过程。// ExportHandler.ashx 导出 Excel public void ProcessRequest(HttpContext context) { string fileName 数据导出_ DateTime.Now.ToString(yyyyMMddHHmmss) .xlsx; // 查询数据实际项目中替换为你的数据访问逻辑 DataTable dt DbHelper.ExecuteDataTable(SELECT Name, Remark, CreateTime FROM T_Item); IWorkbook workbook new XSSFWorkbook(); // 创建 xlsx 工作簿 ISheet sheet workbook.CreateSheet(Sheet1); IRow headerRow sheet.CreateRow(0); // 写表头 for (int i 0; i dt.Columns.Count; i) { headerRow.CreateCell(i).SetCellValue(dt.Columns[i].ColumnName); } // 写数据行 for (int i 0; i dt.Rows.Count; i) { IRow row sheet.CreateRow(i 1); for (int j 0; j dt.Columns.Count; j) { row.CreateCell(j).SetCellValue(dt.Rows[i][j].ToString()); } } // 输出到浏览器 context.Response.Clear(); context.Response.ContentType application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; context.Response.AddHeader(Content-Disposition, attachment;filename HttpUtility.UrlEncode(fileName)); using (MemoryStream ms new MemoryStream()) { workbook.Write(ms); context.Response.BinaryWrite(ms.ToArray()); } context.Response.End(); }这段代码的关键点有三个一是用XSSFWorkbook生成 .xlsx 格式如果要用 .xls 就换成HSSFWorkbook二是文件名用HttpUtility.UrlEncode编码否则中文文件名在部分浏览器会乱码三是最后调用Response.End()终止后续输出避免多余字符混入文件流。数据量特别大时MemoryStream会占用较多内存可以改用Response.OutputStream直接写入。3.3 上传文件前端 filebox 与后端保存的配合EasyUI 的 filebox 组件可以包装一个文件选择框配合 form 提交实现上传。前端写法是input classeasyui-filebox idfile namefile>// 公共 toolbar 封装 function createToolbar(cfg) { return [{ text: 新增, iconCls: icon-add, handler: function () { $(#dlg).dialog(open).dialog(setTitle, 新增); $(#fm).form(clear); cfg.mode add; } }, { text: 修改, iconCls: icon-edit, handler: function () { var row $(#dg).datagrid(getSelected); if (!row) { $.messager.alert(提示, 请选择一行); return; } $(#dlg).dialog(open).dialog(setTitle, 修改); $(#fm).form(load, row); cfg.mode edit; } }, { text: 删除, iconCls: icon-remove, handler: function () { var rows $(#dg).datagrid(getSelections); if (rows.length 0) { $.messager.alert(提示, 请至少选择一行); return; } $.messager.confirm(确认, 确定删除选中的记录吗, function (r) { if (r) { var ids rows.map(function (item) { return item.id; }).join(,); $.post(cfg.deleteUrl, { ids: ids }, function (res) { if (res.success) { $(#dg).datagrid(reload); } else { $.messager.alert(错误, res.msg); } }, json); } }); } }, { text: 导出, iconCls: icon-export, handler: function () { window.location.href cfg.exportUrl ?query encodeURIComponent(JSON.stringify($(#dg).datagrid(options).queryParams)); } }, { text: 上传, iconCls: icon-upload, handler: function () { $(#uploadDlg).dialog(open); } }]; }这个封装把五个操作的通用逻辑抽出来cfg对象里放各个接口地址和模式标识。注意导出那里用了window.location.href而不是 ajax因为文件下载需要浏览器直接处理响应流。上传弹窗单独用一个 dialog里面放 filebox 和提交按钮。5.2 用后端基类减少重复代码C# 这边可以写一个 BaseHandler 继承 IHttpHandler在里面封装 Session 检查、JSON 序列化、异常捕获。具体的业务 Handler 只需要继承它实现HandleRequest方法。这样新增、修改、删除、导出、上传的公共逻辑只写一次。比如public abstract class BaseHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType application/json; try { if (context.Session[User] null) { context.Response.Write({\success\:false,\msg\:\未登录\}); return; } HandleRequest(context); } catch (Exception ex) { // 记录日志 context.Response.Write({\success\:false,\msg\:\ ex.Message.Replace(\, ) \}); } } protected abstract void HandleRequest(HttpContext context); public bool IsReusable { get { return false; } } }业务 Handler 里只写具体的 SQL 和参数处理异常和权限交给基类。这样五个操作的后端代码量能减少三分之一以上。5.3 验证方法用一套测试数据跑通全流程写完代码后不要急着交付。我一般会准备一组测试数据包含特殊字符比如单引号、百分号、空值、超长文本然后依次执行新增、修改、删除、导出、上传。重点观察新增时特殊字符是否被正确转义修改时空值是否覆盖了原值删除时批量操作是否全部成功导出的 Excel 用 Office 和 WPS 分别打开看是否兼容上传的文件名包含中文时是否乱码。这套流程跑下来基本能覆盖 90% 的常见问题。5.4 一个我踩过的坑导出时忘记设置缓冲区大小早期做导出功能时数据量一大就报OutOfMemoryException。后来发现是MemoryStream默认容量太小频繁扩容导致内存碎片。解决办法是在创建 MemoryStream 时预估大小或者直接用Response.OutputStream分块写入。另外NPOI 的workbook.Write方法在数据量超过 10 万行时性能下降明显可以考虑用 SXSSFWorkbookNPOI 的流式写入版本它只保留部分行在内存中。这个坑让我养成了一个习惯凡是涉及文件流的操作先问自己“数据量上限是多少”再决定用哪种写入方式。希望这些经验能帮到你少走一些弯路。本文还有配套的精品资源点击获取