ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

C# Windows三层架构学生系统实战:SQL Server本地部署与增删查改

C# Windows三层架构学生系统实战:SQL Server本地部署与增删查改 简介这是一套基于C# Windows Forms与SQL Server实现的三层架构学生信息管理系统面向.NET初学者及课程设计实践者聚焦数据库应用开发核心能力训练覆盖登录注册、增删查改等典型业务场景。资源包共127个文件含38个C#源码文件BLL/DAL/Model/WinForm各层逻辑清晰、14个DLL与14个PDB调试支持文件、6个CSProj项目配置及1个SLN解决方案文件完整呈现标准三层解耦结构压缩包仅2.26MB轻量易部署。已有367人学习下载配套提供功能演示视频链接便于理解运行效果与交互流程。读者可直接导入Visual Studio运行调试深入掌握ADO.NET数据访问、SQL Server连接配置、WinForm界面事件绑定及分层职责划分等关键实践细节是入门级数据库项目开发的典型参考范例。1. 这不是又一个“Hello World”窗体程序C# Windows三层架构学生系统真能跑通增删查改SQL Server本地部署你可能已经点开过 dozens 个叫“学生管理系统”的 C# 项目双击 exe 却弹出“无法加载 DLL”“数据库连接失败”“找不到 StudentMG.BLL.dll”——不是代码写得不对是它压根没配好三层之间的依赖链和运行时上下文。这个资源不是教学 Demo而是一套可直接编译、带完整 SQL Server 数据库脚本、所有 BLL/DAL/Model 层项目已正确引用、WinForm 界面与业务逻辑完全解耦的实战级工程。它解决的是真实开发中卡死 80% 初学者的三个硬骨头SQL Server 本地实例怎么连、三层项目如何在 VS 里正确生成依赖顺序、WinForm 控件事件如何穿透到 DAL 层执行 SQL 而不爆异常。适合正在学完 C# 基础、刚接触 ADO.NET 和分层思想但被“引用缺失”“连接字符串写错位置”“DataSet 未初始化”反复暴击的开发者。视频演示百度网盘链接里能看到登录后直接进主界面输入学号点查询秒出结果新增学生填完点保存SQL Server Management Studio 里立刻刷新出新记录——没有玄学配置只有可复现的路径。2. 三层架构不是画饼从 Solution 结构到各层职责为什么必须拆成 BLL/DAL/Models2.1 项目结构解析5 个 csproj 文件的真实分工整个 Solution 包含 5 个独立项目不是文件夹堆砌而是 Visual Studio 中真正可单独编译、可单元测试的组件StudentMG.Models.csproj纯数据契约层。只含Student.cs、User.cs等 POCO 类无任何引用零依赖。字段命名严格匹配数据库表列名如StuID对应StuID这是后续SqlDataReader映射不翻车的前提。StudentMG.DAL.csproj数据访问层。引用System.Data.SqlClient注意不是Microsoft.Data.SqlClient此项目基于 .NET Framework 4.7.2。核心是StudentDAL.cs封装了GetAllStudents()、InsertStudent(Student stu)等方法所有 SQL 字符串硬编码在方法内未用 ORM——这是刻意为之让你看清SqlCommand.Parameters.AddWithValue(name, stu.Name)如何防注入而不是被 EF 的黑匣子绕晕。StudentMG.BLL.csproj业务逻辑层。引用StudentMG.Models和StudentMG.DAL。StudentBLL.cs是关键它调用 DAL 方法但做了两件事 DAL 不做① 对Name字段做长度校验if (stu.Name.Length 20) throw new ArgumentException(姓名超长)② 将DateTime.Now赋给CreateTime字段再传给 DAL——说明 BLL 才是规则中心DAL 只管“存”不管“该不该存”。StudentMG.csprojWinForm 表示层。引用StudentMG.BLL和StudentMG.Models绝不直接引用 DAL。MainForm.cs中点击“添加学生”按钮代码是new StudentBLL().AddStudent(stu)而非new StudentDAL().InsertStudent(stu)。这就是三层隔离的铁律UI → BLL → DAL箭头不能反向。StudentMG.BLL.csprojResolveAssemblyReference.cache等缓存文件VS 自动生成切勿手动删除。它们记录了项目间引用的物理路径删了会导致“找不到类型”编译错误——这是新手误删后最常问的坑。提示打开 VS 后先右键 Solution → “设为启动项目”选StudentMG.csproj。若编译报错“未能加载文件或程序集”90% 是因为StudentMG.BLL未正确引用StudentMG.DAL的输出 DLL。解决方案右键StudentMG.BLL→ “添加引用” → “项目”选项卡 → 勾选StudentMG.DAL不是浏览 DLL 文件。2.2 数据库设计SQL Server 2016 兼容的建表脚本与连接字符串落点数据库脚本已打包在DB_Script.sql资源包内包含Students和Users两张表。关键设计点Students.StuID为主键int IDENTITY(1,1)非varchar学号——避免后续排序混乱Students.Name用nvarchar(20)支持中文Users.Password存储SHA256 加密后的哈希值非明文LoginTime记录最后登录时间用于审计。连接字符串必须写在两个地方缺一不可App.config在StudentMG.csproj下configuration connectionStrings add nameStudentDB connectionStringData Source.;Initial CatalogStudentDB;Integrated Securitytrue; providerNameSystem.Data.SqlClient / /connectionStrings /configurationStudentMG.DAL项目的App.config同样位置configuration connectionStrings add nameStudentDB connectionStringData Source.;Initial CatalogStudentDB;Integrated Securitytrue; providerNameSystem.Data.SqlClient / /connectionStrings /configuration注意Data Source.表示本地默认实例。如果你用的是命名实例如SQLEXPRESS必须改成Data SourceYOURPCNAME\\SQLEXPRESS。Integrated Securitytrue表示 Windows 身份验证——这是最简方案无需配置 SQL 登录账户。2.3 WinForm 界面与三层交互以“模糊查询”为例的完整调用链“根据名字模糊查询”功能是检验三层是否打通的黄金用例。我们跟踪从 UI 点击到数据库返回的每一步MainForm.cs中btnSearch_Click事件private void btnSearch_Click(object sender, EventArgs e) { string keyword txtKeyword.Text.Trim(); if (string.IsNullOrEmpty(keyword)) return; // ① UI 层只构造参数不碰业务规则 var students new StudentBLL().SearchStudentsByName(keyword); // ② 绑定到 DataGridViewUI 层不处理 DataSet 或 DataTable dgvStudents.DataSource students; }StudentBLL.cs中SearchStudentsByName方法public ListStudent SearchStudentsByName(string keyword) { // ③ BLL 层加业务规则关键词至少2个字符 if (keyword.Length 2) throw new ArgumentException(搜索关键词至少2个字符); // ④ 调用 DAL传递参数 return new StudentDAL().SearchStudentsByName(keyword); }StudentDAL.cs中SearchStudentsByName方法public ListStudent SearchStudentsByName(string keyword) { var list new ListStudent(); using (var conn new SqlConnection(ConfigurationManager.ConnectionStrings[StudentDB].ConnectionString)) { conn.Open(); // ⑤ DAL 层写 SQL用参数化防止注入 using (var cmd new SqlCommand(SELECT * FROM Students WHERE Name LIKE keyword, conn)) { cmd.Parameters.AddWithValue(keyword, % keyword %); // 模糊匹配 using (var reader cmd.ExecuteReader()) { while (reader.Read()) { list.Add(new Student { StuID Convert.ToInt32(reader[StuID]), Name reader[Name].ToString(), Gender reader[Gender].ToString(), Class reader[Class].ToString(), BirthDate Convert.ToDateTime(reader[BirthDate]) }); } } } } return list; }关键逻辑说明cmd.Parameters.AddWithValue(keyword, % keyword %)是安全写法%由 C# 拼接SQL 中LIKE keyword的keyword是完整值如%张三%避免 SQL 注入Convert.ToDateTime(reader[BirthDate])强制类型转换若数据库该列为NULL此处会抛异常——所以实际项目中需用reader.IsDBNull(BirthDate) ? null : Convert.ToDateTime(...)本项目为简化未处理但你必须知道这个坑。3. 编译与运行前必做的五项检查从 VS 版本到 SQL Server 实例状态3.1 开发环境硬性要求VS 2017 与 SQL Server LocalDB / ExpressVisual Studio 版本必须 ≥ VS 2017因项目使用.csproj新格式VS 2015 无法加载。推荐 VS 2019 Community免费安装时勾选“.NET 桌面开发”工作负载。SQL Server 版本支持 SQL Server 2016 及以上包括SQL Server Express免费官网下载SQL Server LocalDBVS 安装时自带轻量级适合开发Azure SQL Database不推荐本项目未适配云连接字符串。验证 SQL Server 是否就绪打开 SSMSSQL Server Management Studio→ 连接服务器 → 输入.本地默认实例或(localdb)\mssqllocaldbLocalDB→ 若能连上并看到master数据库则 OK。若提示“拒绝访问”说明 SQL Server 服务未启动Win R→services.msc→ 找到SQL Server (MSSQLSERVER)或SQL Server (SQLEXPRESS)→ 右键“启动”。3.2 数据库初始化执行 DB_Script.sql 的三步操作在 SSMS 中新建查询窗口粘贴DB_Script.sql全部内容含CREATE DATABASE StudentDB关键步骤执行前确保当前数据库上下文是master左上角下拉框选master否则CREATE DATABASE会失败执行后刷新对象资源管理器确认出现StudentDB数据库 → 展开 →Tables→ 应有Students和Users表。提示DB_Script.sql中INSERT INTO Users语句预置了管理员账号INSERT INTO Users (Username, Password, Role) VALUES (admin, 8d969eef6ecad8b8a2c9986e724192d185834f9152455544e0d52b1a5135451e, Admin)密码admin的 SHA256 哈希值已给出登录时用户名输admin密码输admin即可。3.3 项目引用修复当 VS 报“找不到命名空间”时的急救指南常见报错The type or namespace name StudentBLL could not be found。这不是代码错是引用断了。按顺序检查检查项正确做法错误做法BLL 引用 DAL右键StudentMG.BLL→ “添加引用” → “项目” → 勾选StudentMG.DAL浏览到bin\Debug\StudentMG.DAL.dll手动添加UI 引用 BLL右键StudentMG→ “添加引用” → “项目” → 勾选StudentMG.BLL直接using StudentMG.DALUI 层禁止引用 DAL所有项目 Target Framework右键每个项目 → “属性” → “应用程序” → “目标框架” → 统一为.NET Framework 4.7.2有的选 4.5有的选 4.8版本不一致导致引用失效注意若StudentMG.DAL编译失败如SqlConnection报红说明缺少System.Data.SqlClient引用。右键StudentMG.DAL→ “添加引用” → “程序集” → 搜索System.Data→ 勾选。4. 避坑五个血泪经验总结——为什么你的三层系统总在登录后崩溃4.1 现象点击“登录”按钮后程序闪退VS 输出窗口显示System.NullReferenceException: 未将对象引用设置到对象的实例原因MainForm.cs中btnLogin_Click方法里new StudentBLL().ValidateUser(username, password)返回null但代码直接.Role调用未判空。解决在StudentBLL.ValidateUser方法末尾加判空public User ValidateUser(string username, string password) { var user new StudentDAL().GetUserByUsername(username); if (user null) return null; // ← 关键DAL 返回 null 时BLL 必须透传 if (user.Password HashHelper.SHA256(password)) return user; return null; }UI 层调用时var user new StudentBLL().ValidateUser(txtUser.Text, txtPass.Text); if (user ! null user.Role Admin) this.Hide(); // 登录成功 else MessageBox.Show(用户名或密码错误);4.2 现象新增学生后DataGridView 显示空白但数据库里已有记录原因dgvStudents.DataSource students后未调用dgvStudents.Refresh()且AutoGenerateColumns属性为FalseWinForm 默认导致列未自动创建。解决在btnAdd_Click方法末尾加两行dgvStudents.AutoGenerateColumns true; // 让 DataGridView 自动映射 Student 属性 dgvStudents.Refresh(); // 强制重绘4.3 现象修改学生信息时BirthDate字段报System.FormatException: String was not recognized as a valid DateTime原因WinForm 的DateTimePicker控件默认值为1/1/0001若用户未选择日期dtpBirthDate.Value仍为该值而 SQL Serverdatetime类型最小值为1/1/1753。解决在btnUpdate_Click中加校验if (dtpBirthDate.Checked false) // 用户未勾选日期 { MessageBox.Show(请选择出生日期); return; } if (dtpBirthDate.Value new DateTime(1753, 1, 1)) { MessageBox.Show(日期不能早于1753年1月1日); return; }4.4 现象删除学生后再次查询被删学生仍显示在 DataGridView 中原因dgvStudents.DataSource绑定的是ListStudent删除操作只影响数据库未刷新 UI 的 DataSource。解决删除后重新查询并绑定private void btnDelete_Click(object sender, EventArgs e) { if (dgvStudents.SelectedRows.Count 0) return; int stuId Convert.ToInt32(dgvStudents.SelectedRows[0].Cells[StuID].Value); new StudentBLL().DeleteStudent(stuId); // ← 关键重新加载数据 dgvStudents.DataSource new StudentBLL().GetAllStudents(); dgvStudents.Refresh(); }4.5 现象程序首次运行时App.config中的连接字符串读取为空报NullReferenceException原因ConfigurationManager.ConnectionStrings[StudentDB]返回null因为App.config未被正确复制到输出目录。解决右键App.config→ “属性” → “复制到输出目录” → 设为 “始终复制”。编译后检查bin\Debug\StudentMG.exe.config是否存在且内容完整。5. 进阶技巧把“增删查改”变成可维护的模块——动态 SQL 构建与事务封装5.1 动态查询构建支持多条件组合查询非仅按姓名原项目只支持单字段模糊查询实际业务常需“姓名班级性别”组合筛选。硬编码 SQL 会爆炸式增长改用StringBuilder动态拼接// 在 StudentDAL.cs 中新增方法 public ListStudent SearchStudentsAdvanced(string name, string classNo, string gender) { var sql new StringBuilder(SELECT * FROM Students WHERE 11); var parameters new ListSqlParameter(); if (!string.IsNullOrEmpty(name)) { sql.Append( AND Name LIKE name); parameters.Add(new SqlParameter(name, % name %)); } if (!string.IsNullOrEmpty(classNo)) { sql.Append( AND Class classNo); parameters.Add(new SqlParameter(classNo, classNo)); } if (!string.IsNullOrEmpty(gender)) { sql.Append( AND Gender gender); parameters.Add(new SqlParameter(gender, gender)); } var list new ListStudent(); using (var conn new SqlConnection(ConfigurationManager.ConnectionStrings[StudentDB].ConnectionString)) { conn.Open(); using (var cmd new SqlCommand(sql.ToString(), conn)) { cmd.Parameters.AddRange(parameters.ToArray()); using (var reader cmd.ExecuteReader()) { while (reader.Read()) { list.Add(new Student { /* ... */ }); } } } } return list; }参数说明WHERE 11是技巧避免判断AND时首条件缺失导致语法错误parameters.Add(...)确保每个条件都参数化杜绝注入cmd.Parameters.AddRange(...)批量添加比循环AddWithValue更高效。5.2 事务封装保证“添加学生记录操作日志”原子性学生信息新增时常需同时写Students表和OperationLog表。若前者成功后者失败数据不一致。用SqlTransaction封装// 在 StudentDAL.cs 中 public bool AddStudentWithLog(Student stu, string operatorName) { using (var conn new SqlConnection(ConfigurationManager.ConnectionStrings[StudentDB].ConnectionString)) { conn.Open(); using (var trans conn.BeginTransaction()) // ← 开启事务 { try { // 步骤1插入学生 using (var cmd1 new SqlCommand(INSERT INTO Students(...) VALUES(...), conn, trans)) { // 设置参数... cmd1.ExecuteNonQuery(); } // 步骤2插入日志 using (var cmd2 new SqlCommand(INSERT INTO OperationLog(...) VALUES(...), conn, trans)) { // 设置参数... cmd2.ExecuteNonQuery(); } trans.Commit(); // ← 全部成功才提交 return true; } catch { trans.Rollback(); // ← 任一失败则回滚 return false; } } } }关键点new SqlCommand(..., conn, trans)的第三个参数必须传trans否则命令不在事务内执行。5.3 配置驱动的连接字符串从硬编码走向可配置化当前连接字符串写死在App.config但生产环境常需切换数据库。升级为ConfigurationManager.AppSettings修改App.configappSettings add keyDBServer value. / add keyDBName valueStudentDB / add keyUseWindowsAuth valuetrue / /appSettings在StudentDAL.cs中动态构建连接字符串private string GetConnectionString() { string server ConfigurationManager.AppSettings[DBServer]; string dbName ConfigurationManager.AppSettings[DBName]; bool useWindowsAuth bool.Parse(ConfigurationManager.AppSettings[UseWindowsAuth]); if (useWindowsAuth) return $Data Source{server};Initial Catalog{dbName};Integrated Securitytrue;; else return $Data Source{server};Initial Catalog{dbName};User IDsa;Passwordyourpass;; }优势无需改代码只需改App.config即可切换开发/测试/生产数据库。从那以后我每次新建三层项目都强制走一遍这三步① 先写Models层 POCO 类生成数据库脚本② 再写DAL层每个方法只做一件事查/增/删/改不加业务逻辑③ 最后写BLL层把校验、计算、日志全塞进去。UI 层永远只调用 BLL绝不碰 DAL。这套流程让我在客户现场部署时三天内交付可维护的系统而不是交一个“能跑但不敢改”的黑匣子。希望帮到你。本文还有配套的精品资源点击获取
返回列表