ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

C# WinForm 连接 SQL Server 数据库课设实战:增删改查与安全优化

2026/10/3 1:15:40 拓冰建站 浏览量
C# WinForm 连接 SQL Server 数据库课设实战:增删改查与安全优化 简介这份资源是安徽工业大学《数据库系统概论》课程设计报告书面向信息与计算科学等专业学生及需要完成数据库课程设计的读者围绕基于Windows环境的学生成绩管理系统展开解决从需求分析到开发落地的完整设计问题。压缩包内共1个doc文档约532KB内容涵盖开发环境与工具介绍、系统需求分析与设计、功能模块图、系统实现、测试优化及参考文献等章节完整呈现了使用Visual Studio 2013与SQL Server 2008构建C/S架构系统的全过程。报告详细描述了用户登录、学生信息查询与管理、成绩录入与增删改查、新用户注册及帮助菜单等模块的设计思路并涉及SQL语句应用、数据安全性与界面设计等细节。目前已有68人学习适合作为课程设计参考模板帮助读者掌握数据库理论知识与实际开发技能。1. 一份 2016 年的数据库课设报告为什么现在还有人翻出来看翻课程设计资料的人多半不是真想照着做一个学生成绩管理系统。真实诉求往往是手头有个数据库课设要交想找一份结构完整、能跑通、有源码有文档的参考或者刚学完 SQL 语句想找个真实项目把 C# 窗体、SQL Server 建库、增删改查这几件事串起来。这份《安工大数据库课程设计报告书》正好卡在这个位置上——它是一份完整的实验报告从需求分析、E-R 图、功能模块图一路写到具体实现附录里还贴了增加学生信息和用户登录两段核心源码。它的技术栈很明确Visual Studio 2013 做 C# WinForm 窗体SQL Server 2008 建库C/S 架构功能覆盖登录、学生信息增删改查、成绩录入、新用户注册、帮助菜单。说白了就是一个典型的教学级 CRUD 系统。别指望它有什么高并发设计或者 ORM 框架但作为课设参考它的文档结构、表设计思路、ADO.NET 直连写法恰好是老师想看到的那套东西。适合两类人一是赶课设 deadline 需要一份能对着改的模板二是想搞明白 WinForm 连 SQL Server 到底怎么写代码的新手。2. 从报告到可运行系统环境搭建与数据库建表2.1 为什么是 VS2013 SQL Server 2008 这套组合先说服自己一件事这套环境不是随便选的。2016 年前后的高校机房装的基本就是 VS2013 和 SQL Server 2008老师验收也在这个环境里跑。你现在用 VS2022 打开这份代码大概率会遇到项目文件格式不兼容、.NET Framework 版本对不上的问题。所以最省事的做法是环境对齐而不是硬升级。具体来说VS2013 对应 .NET Framework 4.5WinForm 项目模板和控件布局跟现在差别不大但 NuGet 包管理器和 C# 语言版本有差异。SQL Server 2008 的 T-SQL 语法跟 2019/2022 基本兼容但安装配置和连接字符串写法要注意。常见做法是如果机房有 VS2013 就用 VS2013没有的话用 VS2019 打开时选「重定向项目」把目标框架改成 4.7.2 也能跑但窗体设计器偶尔会抽风。连接字符串是第一个卡点。报告里用的是open.constring这个静态变量说明作者把连接串抽到了一个公共类里。我一般会这样写// 放在一个叫 open.cs 的公共类里 public class open { // Data Source 填服务器名本机可用 . 或 localhost // Initial Catalog 填数据库名 // Integrated SecurityTrue 表示用 Windows 身份验证省去账号密码 public static string constring Data Source.;Initial CatalogStudentScoreDB;Integrated SecurityTrue; }逻辑说明Data Source.里的点代表本机默认实例如果装的是命名实例比如 SQLEXPRESS要写成.\SQLEXPRESS。Integrated SecurityTrue走 Windows 身份验证不用在代码里写 sa 账号密码避免明文泄露。如果非要用 SQL 账号改成User IDsa;Password你的密码但课设报告里不建议这么写老师会问安全性。参数怎么改数据库名StudentScoreDB要跟你实际建的库名一致如果 SQL Server 没开 TCP/IP 协议连接会报「无法连接到服务器」去 SQL Server 配置管理器里把 TCP/IP 启用重启服务。2.2 三张核心表的建表语句与字段设计报告里提到了登录密码表、学生信息表、成绩表但没给完整建表语句。按它的功能描述我补一套能直接跑的-- 登录表存用户名和密码 CREATE TABLE Login ( LoginID INT IDENTITY(1,1) PRIMARY KEY, LoginName NVARCHAR(50) NOT NULL, Password NVARCHAR(50) NOT NULL ); -- 学生信息表报告里叫 Table_1字段有 Name、sex、Age CREATE TABLE Table_1 ( StudentID INT IDENTITY(1,1) PRIMARY KEY, Name NVARCHAR(50) NOT NULL, sex NVARCHAR(10) NOT NULL, Age INT NOT NULL, Major NVARCHAR(50) NULL ); -- 成绩表关联学生存各科成绩 CREATE TABLE Score ( ScoreID INT IDENTITY(1,1) PRIMARY KEY, StudentID INT NOT NULL, CourseName NVARCHAR(50) NOT NULL, Score DECIMAL(5,2) NOT NULL, FOREIGN KEY (StudentID) REFERENCES Table_1(StudentID) );逻辑说明IDENTITY(1,1)让主键自增省去手动编号。NVARCHAR而不是VARCHAR因为姓名和专业可能含中文NVARCHAR 按 Unicode 存储不会乱码。成绩用DECIMAL(5,2)能存到小数点后两位比 FLOAT 精确避免 59.99999 这种玄学问题。参数说明Age字段在源码里做了 0 到 100 的校验建表时用 INT 就够。外键约束FOREIGN KEY保证成绩表里的 StudentID 一定存在于学生表删学生时如果有成绩记录会报错这是好事防止误删。注意SQL Server 2008 建表时如果报「列名无效」检查是不是把表名写成了保留字比如 Order、User 这类Table_1 这种命名虽然丑但确实避开了关键字冲突。2.3 用 ADO.NET 打通窗体与数据库报告附录里的源码用的是最原始的 ADO.NETSqlConnection开连接SqlCommand执行 SQLExecuteNonQuery返回受影响行数ExecuteScalar取单值。这套写法现在看很笨但课设答辩时老师就认这个因为能看出你懂底层。登录模块的完整流程是这样的private void btnok_Click(object sender, EventArgs e) { // 用 using 确保连接释放比 try-finally 更简洁 using (SqlConnection conn new SqlConnection(open.constring)) { try { conn.Open(); // 参数化查询防止 SQL 注入 string sql SELECT COUNT(*) FROM Login WHERE LoginNamename AND Passwordpsw; SqlCommand comm new SqlCommand(sql, conn); comm.Parameters.AddWithValue(name, txtname.Text.Trim()); comm.Parameters.AddWithValue(psw, txtpsw.Text.Trim()); int count (int)comm.ExecuteScalar(); if (count 1) { student stu new student(); stu.Show(); this.Visible false; } else { MessageBox.Show(用户名或密码不正确); } } catch (Exception ex) { MessageBox.Show(数据库连接失败 ex.Message); } } }逻辑说明报告原文用的是string.Format拼 SQL这是典型的 SQL 注入漏洞写法输入 OR 11就能绕过登录。我改成Parameters.AddWithValue参数化查询这是必须纠正的地方。ExecuteScalar返回查询结果第一行第一列COUNT(*)正好返回匹配行数等于 1 说明账号密码对上了。参数说明txtname.Text.Trim()去掉首尾空格防止用户复制粘贴时带空格导致登录失败。this.Visible false隐藏登录窗体而不是关闭因为关闭主窗体时如果登录窗体已销毁整个程序会退出。3. 增删改查四个模块的代码实现与参数校验3.1 增加学生信息从界面控件到 INSERT 语句增加模块是课设里最常被检查的部分因为它涉及控件取值、类型转换、SQL 插入、异常处理一整条链路。报告附录里给了完整代码但有几个地方需要改。先看界面逻辑cbosex下拉框在窗体加载时填充「男」「女」默认选中第一项。这个用Add_Load事件做private void Add_Load(object sender, EventArgs e) { cbosex.Items.Clear(); cbosex.Items.Add(男); cbosex.Items.Add(女); cbosex.SelectedIndex 0; // 默认选中男 }然后是增加按钮的核心逻辑我在报告基础上做了参数化改造private void btnadd_Click(object sender, EventArgs e) { // 先做输入校验年龄必须是数字且在合理范围 int age; if (!int.TryParse(txtage.Text.Trim(), out age)) { MessageBox.Show(年龄必须输入数字); return; } if (age 0 || age 100) { MessageBox.Show(年龄需在 0 到 100 之间); return; } if (string.IsNullOrEmpty(txtname.Text.Trim())) { MessageBox.Show(姓名不能为空); return; } using (SqlConnection conn new SqlConnection(open.constring)) { try { conn.Open(); string sql INSERT INTO Table_1 (Name, sex, Age) VALUES (name, sex, age); SqlCommand comm new SqlCommand(sql, conn); comm.Parameters.AddWithValue(name, txtname.Text.Trim()); comm.Parameters.AddWithValue(sex, cbosex.Text); comm.Parameters.AddWithValue(age, age); int rows comm.ExecuteNonQuery(); if (rows 1) { MessageBox.Show(插入成功); this.Close(); } else { MessageBox.Show(插入失败); } } catch (Exception ex) { MessageBox.Show(操作异常 ex.Message); } } }逻辑说明报告原文用int.Parse(txtage.Text)直接转换用户输入非数字会抛FormatException程序直接崩。改成int.TryParse返回布尔值转换失败给提示而不是崩溃。ExecuteNonQuery返回受影响行数插入成功返回 1。参数说明AddWithValue会自动推断参数类型age传 int 进去SQL Server 按 INT 处理。如果表里 Age 字段是 NVARCHAR这里传 int 也能隐式转换但不推荐字段类型和参数类型最好一致。3.2 删除与修改WHERE 条件写错就是灾难删除和修改共用一套逻辑先定位记录再执行操作。区别在于 DELETE 不可恢复UPDATE 可以改回来。报告里删除功能写的是「一旦删除就会删除所有学生的信息」这明显是 WHERE 条件缺失或者写成了恒真条件属于血泪级 bug。正确的删除写法private void btndelete_Click(object sender, EventArgs e) { // 从 ListView 选中项拿到学号没有选中就提示 if (listView1.SelectedItems.Count 0) { MessageBox.Show(请先选中要删除的学生); return; } string studentId listView1.SelectedItems[0].SubItems[0].Text; // 二次确认防止误点 DialogResult result MessageBox.Show( 确定删除学号为 studentId 的学生吗, 删除确认, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (result ! DialogResult.Yes) return; using (SqlConnection conn new SqlConnection(open.constring)) { try { conn.Open(); // 先删成绩表里的关联记录再删学生表否则外键约束会报错 string sqlScore DELETE FROM Score WHERE StudentIDsid; SqlCommand commScore new SqlCommand(sqlScore, conn); commScore.Parameters.AddWithValue(sid, studentId); commScore.ExecuteNonQuery(); string sqlStu DELETE FROM Table_1 WHERE StudentIDsid; SqlCommand commStu new SqlCommand(sqlStu, conn); commStu.Parameters.AddWithValue(sid, studentId); int rows commStu.ExecuteNonQuery(); if (rows 1) { MessageBox.Show(删除成功); // 刷新列表 LoadStudentList(); } } catch (Exception ex) { MessageBox.Show(删除失败 ex.Message); } } }逻辑说明WHERE StudentIDsid是命门少了这个条件就是全表删除。先删子表 Score 再删主表 Table_1因为外键约束不允许删除被引用的主表记录。SelectedItems[0].SubItems[0].Text取 ListView 第一列的值前提是填充 ListView 时学号在第一列。参数说明MessageBoxButtons.YesNo弹确认框用户点「是」才继续。MessageBoxIcon.Warning显示警告图标给用户视觉提醒。LoadStudentList()是自定义的刷新方法删完重新查一遍数据库填充列表。修改功能跟删除类似把 DELETE 换成 UPDATEUPDATE Table_1 SET Namename, sexsex, Ageage WHERE StudentIDsid3.3 查询与排序LIKE 模糊匹配和 ORDER BY 的坑报告里说支持按学号、姓名、专业查询这需要动态拼接 WHERE 条件。常见做法是用 LIKE 做模糊匹配private void btnsearch_Click(object sender, EventArgs e) { string keyword txtsearch.Text.Trim(); if (string.IsNullOrEmpty(keyword)) { MessageBox.Show(请输入查询关键词); return; } using (SqlConnection conn new SqlConnection(open.constring)) { try { conn.Open(); // LIKE 配合 % 实现模糊查询kw 参数化防止注入 string sql SELECT StudentID, Name, sex, Age FROM Table_1 WHERE Name LIKE kw OR Major LIKE kw ORDER BY StudentID; SqlCommand comm new SqlCommand(sql, conn); comm.Parameters.AddWithValue(kw, % keyword %); SqlDataAdapter adapter new SqlDataAdapter(comm); DataTable dt new DataTable(); adapter.Fill(dt); // 绑定到 DataGridView 或手动填充 ListView dataGridView1.DataSource dt; } catch (Exception ex) { MessageBox.Show(查询失败 ex.Message); } } }逻辑说明LIKE kw配合%keyword%实现包含匹配用户输入「张」能查出「张三」「张伟」。ORDER BY StudentID按学号排序保证结果稳定。用SqlDataAdapter填充DataTable再绑到DataGridView比手动逐行填充 ListView 省事。参数说明%是通配符%keyword%表示前后都可以有任意字符。如果只想匹配开头写成keyword %。DataGridView的DataSource赋值为 DataTable 后会自动生成列列名就是 SQL 查询的字段名。注意如果查询结果为空DataGridView会显示空白最好加一句判断if (dt.Rows.Count 0) MessageBox.Show(没有找到匹配记录);不然用户以为程序卡死了。4. 课设答辩前必须排查的五个坑4.1 连接字符串写错导致「无法连接到服务器」现象程序一启动就弹「在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误」。原因Data Source填的服务器名不对或者 SQL Server 服务没启动或者 TCP/IP 协议没启用。解决打开 SQL Server 配置管理器确认 SQL Server 服务正在运行展开「SQL Server 网络配置」把 TCP/IP 状态改为「已启用」重启 SQL Server 服务。连接字符串里本机默认实例用.命名实例用.\实例名。4.2 中文姓名插入数据库变成问号现象插入「张三」数据库里显示??。原因数据库字段类型用了VARCHAR而不是NVARCHAR或者连接字符串没指定字符集。解决建表时所有存中文的字段用NVARCHAR。连接字符串加CharsetUTF-8不是 SQL Server 的写法SQL Server 靠 NVARCHAR 类型本身支持 Unicode不需要额外指定。4.3 删除按钮点一下全表清空现象点删除所有学生记录都没了。原因DELETE 语句没写 WHERE 条件或者 WHERE 条件恒真比如WHERE 11。解决DELETE 必须带 WHERE且 WHERE 后面的字段值来自用户选中的记录。执行前弹确认框执行后检查ExecuteNonQuery返回值如果返回行数等于总记录数说明条件有问题。4.4 登录绕过输入 OR 11直接进系统现象不输正确密码也能登录。原因用string.Format拼接 SQL用户输入被当成 SQL 代码执行。解决全部改用SqlParameter参数化查询。这是课设里最容易被老师挑出来的安全问题改起来不费事但能体现你懂安全。4.5 窗体关闭后程序还在后台跑现象关掉主窗体任务管理器里进程还在。原因登录窗体用this.Hide()隐藏而不是关闭主窗体关闭时没有退出整个应用程序。解决在 Program.cs 的Main方法里把Application.Run(new Login())改成Application.Run(new Login())并在主窗体关闭事件里调用Application.Exit()。或者登录成功后this.Close()但要注意主窗体关闭时程序自动退出。5. 把课设报告变成能写进简历的项目课设交完就扔是最大的浪费。这份报告里的学生成绩管理系统稍微改改就能变成面试时能聊的东西。我的习惯是交完课设后花一个周末做三件事——加一层简单的分层、补一个导出功能、把 SQL 注入和明文密码这两个硬伤修掉。分层不用上什么高大上的架构把数据库操作从窗体代码里抽出来就行。建一个StudentDAL.cs把增删改查封装成方法public class StudentDAL { // 查询所有学生返回 DataTable public static DataTable GetAllStudents() { using (SqlConnection conn new SqlConnection(open.constring)) { string sql SELECT StudentID, Name, sex, Age, Major FROM Table_1 ORDER BY StudentID; SqlDataAdapter adapter new SqlDataAdapter(sql, conn); DataTable dt new DataTable(); adapter.Fill(dt); return dt; } } // 按学号删除返回是否成功 public static bool DeleteStudent(int studentId) { using (SqlConnection conn new SqlConnection(open.constring)) { conn.Open(); // 先删成绩再删学生事务保证一致性 using (SqlTransaction trans conn.BeginTransaction()) { try { string sqlScore DELETE FROM Score WHERE StudentIDsid; SqlCommand cmdScore new SqlCommand(sqlScore, conn, trans); cmdScore.Parameters.AddWithValue(sid, studentId); cmdScore.ExecuteNonQuery(); string sqlStu DELETE FROM Table_1 WHERE StudentIDsid; SqlCommand cmdStu new SqlCommand(sqlStu, conn, trans); cmdStu.Parameters.AddWithValue(sid, studentId); int rows cmdStu.ExecuteNonQuery(); trans.Commit(); return rows 1; } catch { trans.Rollback(); return false; } } } } }逻辑说明SqlTransaction保证两条 DELETE 要么都成功要么都回滚避免删了成绩但学生没删掉的数据不一致。using包裹连接和事务代码块结束时自动释放资源。窗体里只负责取控件值、调 DAL 方法、显示结果逻辑清晰答辩时也好讲。导出功能用StreamWriter写 CSV 就行不需要引入第三方库public static void ExportToCsv(DataTable dt, string filePath) { using (StreamWriter sw new StreamWriter(filePath, false, Encoding.UTF8)) { // 写表头 string[] headers new string[dt.Columns.Count]; for (int i 0; i dt.Columns.Count; i) headers[i] dt.Columns[i].ColumnName; sw.WriteLine(string.Join(,, headers)); // 写数据行 foreach (DataRow row in dt.Rows) { string[] cells new string[dt.Columns.Count]; for (int i 0; i dt.Columns.Count; i) cells[i] row[i].ToString(); sw.WriteLine(string.Join(,, cells)); } } }逻辑说明StreamWriter的第二个参数false表示覆盖写入而不是追加第三个参数Encoding.UTF8保证中文不乱码。string.Join把数组拼成逗号分隔的字符串Excel 能直接打开。密码明文存储的问题课设阶段不用上 bcrypt用 SHA256 加个盐就够public static string HashPassword(string password, string salt) { using (SHA256 sha SHA256.Create()) { string combined password salt; byte[] bytes sha.ComputeHash(Encoding.UTF8.GetBytes(combined)); return Convert.ToBase64String(bytes); } }逻辑说明salt是每个用户随机生成的字符串存在 Login 表里。登录时把用户输入的密码加盐再哈希跟数据库里存的哈希值比对。这样即使数据库泄露攻击者也不能直接拿到明文密码。从那以后我每次做完课设都会强制走一遍「参数化查询检查、密码哈希检查、事务完整性检查」这三步。课设代码可以简单但不能有硬伤因为面试官翻你 GitHub 的时候第一眼看的不是功能多花哨而是有没有 SQL 注入、有没有明文密码、删除有没有 WHERE。这份报告的价值不在于它多完美而在于它给了一个完整的骨架你在这个骨架上改掉几个关键问题它就能从「交作业」变成「能聊的项目」。希望帮到你。本文还有配套的精品资源点击获取