一、项目概述
本程序实现了一个基于SQL Server数据库的学员信息管理工具,主要功能包括:
添加学员:录入编号、姓名、性别、年龄
修改学员:根据学员编号修改其性别和年龄
删除学员:支持按姓名删除、按编号删除
界面使用WinForms设计,分为三个区域,如下图:
二、整体思路
拿到需求后,我的设计步骤如下:
数据库设计:创建
stuInfo表,包含stuNo(学员编号,主键)、stuName、stuSex、stuAge四个字段。界面布局:用Panel分区,使用TextBox输入文本,ComboBox选择年龄(15-30岁),RadioButton选择性别。
数据操作:采用ADO.NET的
SqlConnection、SqlCommand,编写SQL语句实现增删改。异常处理与验证:对用户输入做非空校验,操作前先查询数据是否存在,避免直接操作失败。
三、准备工作
IDE:Visual Studio 2019/2022
.NET版本:.NET Framework 4.5+
数据库:SQL Server 2012+(本例使用本地实例
server=.)数据库表脚本:
CREATE DATABASE student; GO USE student; GO CREATE TABLE stuInfo ( stuNo NVARCHAR(20) PRIMARY KEY, stuName NVARCHAR(50) NOT NULL, stuSex NCHAR(1) CHECK(stuSex IN ('男','女')), stuAge INT CHECK(stuAge BETWEEN 15 AND 30) ); GO四、核心代码解析
1. 窗体加载 – 初始化年龄下拉框
在Form1_Load事件中,为两个年龄ComboBox动态添加15~30的数字。
private void Form1_Load(object sender, EventArgs e) { // 添加学员区的年龄下拉框 for (int i = 15; i <= 30; i++) { comboBox_age.Items.Add(i); } comboBox_age.SelectedIndex = 0; // 默认选中15 // 修改学员区的年龄下拉框 for (int i = 15; i <= 30; i++) { comboBox_exAge.Items.Add(i); } }思路:固定范围选择避免了用户输入非法年龄。
2. 添加学员 – 完整业务流程
步骤:
非空校验(编号、姓名、性别)
从控件取值,年龄需转为
int连接数据库执行
INSERT成功后清空表单
关键代码:
private void button_add_Click(object sender, EventArgs e) { // 1. 非空判断 if (string.IsNullOrWhiteSpace(textBox_ID.Text)) { MessageBox.Show("请输入学员编号!"); return; } // ... 姓名、性别校验(性别必须二选一) string stuId = textBox_ID.Text; string stuName = textBox_name.Text; string stuSex = radioB_man.Checked ? "男" : "女"; int stuAge = Convert.ToInt32(comboBox_age.SelectedItem); // 2. 数据库插入 using (SqlConnection con = new SqlConnection(@"server=.;uid=sa;pwd=123;database=student")) { con.Open(); string sql = $"INSERT INTO stuInfo VALUES ('{stuId}','{stuName}','{stuSex}',{stuAge})"; using (SqlCommand cmd = new SqlCommand(sql, con)) { int result = cmd.ExecuteNonQuery(); if (result > 0) { MessageBox.Show("添加成功"); ClearAddForm(); // 自定义清空方法 } else MessageBox.Show("添加失败"); } } }⚠️注意:代码中性别获取逻辑存在隐患 – 若两个RadioButton都未选中,会默认“女”。实际应该先判断,提示用户选择。我后来优化成了if-else结构。
3. 修改学员 – 先查后改
修改的核心思路:根据用户输入的编号,先查询数据库中是否存在该学员,若存在再执行UPDATE。
private void button_exchange_Click(object sender, EventArgs e) { // 非空校验 if (string.IsNullOrWhiteSpace(textBox_exID.Text)) { MessageBox.Show("请输入要修改的学员编号!"); return; } string exstuID = textBox_exID.Text; using (SqlConnection con = new SqlConnection(connectionString)) { con.Open(); // 第一步:查是否存在 string checkSql = $"SELECT COUNT(*) FROM stuInfo WHERE stuNo='{exstuID}'"; using (SqlCommand checkCmd = new SqlCommand(checkSql, con)) { int count = (int)checkCmd.ExecuteScalar(); if (count == 0) { MessageBox.Show("查询不到此学员编号!"); return; } } // 第二步:获取新值(性别、年龄) string newSex = radioB_exMan.Checked ? "男" : "女"; int newAge = Convert.ToInt32(comboBox_exAge.SelectedItem); // 第三步:更新 string updateSql = $"UPDATE stuInfo SET stuSex='{newSex}', stuAge={newAge} WHERE stuNo='{exstuID}'"; using (SqlCommand cmd = new SqlCommand(updateSql, con)) { int result = cmd.ExecuteNonQuery(); if (result > 0) { MessageBox.Show("修改成功!"); ClearModifyForm(); } else MessageBox.Show("修改失败!"); } } }4. 删除学员 – 按姓名 / 按编号
两个删除逻辑高度相似:校验非空 → 验证存在 → 执行DELETE。以按姓名删除为例:
private void button_delName_Click_1(object sender, EventArgs e) { if (string.IsNullOrWhiteSpace(textBox_delName.Text)) { MessageBox.Show("请输入要删除的姓名!"); return; } using (SqlConnection con = new SqlConnection(connectionString)) { con.Open(); // 验证姓名是否存在 string checkSql = $"SELECT COUNT(*) FROM stuInfo WHERE stuName='{textBox_delName.Text}'"; using (SqlCommand cmd = new SqlCommand(checkSql, con)) { int count = (int)cmd.ExecuteScalar(); if (count == 0) { MessageBox.Show("你输入的姓名不存在!"); return; } } // 执行删除(注意:会删除所有同名的记录!) string delSql = $"DELETE FROM stuInfo WHERE stuName='{textBox_delName.Text}'"; using (SqlCommand cmd = new SqlCommand(delSql, con)) { int count = cmd.ExecuteNonQuery(); if (count > 0) MessageBox.Show("删除成功!"); } } }按编号删除代码类似,这里不再重复。
五、开发中遇到的问题 & 反思
🔴 问题1:SQL注入风险
整个项目中使用的是字符串拼接SQL,例如:
$"SELECT COUNT(*) FROM stuInfo WHERE stuNo='{exstuID}'"如果用户在文本框输入' OR '1'='1,后果不堪设想。这是严重的安全隐患。
✅解决方案:使用参数化查询。例如:
string sql = "INSERT INTO stuInfo VALUES (@no,@name,@sex,@age)"; SqlCommand cmd = new SqlCommand(sql, con); cmd.Parameters.AddWithValue("@no", stuId); // ... 其他参数🔴 问题2:数据库连接字符串硬编码
代码中直接写死了server=.;uid=sa;pwd=123;database=student,一旦数据库密码或服务器变更,必须重新编译程序。
✅改进:将连接字符串写入App.config配置文件,通过ConfigurationManager读取。
🔴 问题3:控件事件绑定错误
设计文件中,button3被命名为“修改”按钮,却绑定了button_exchange_Click事件(这个事件是修改学员区的)。这是个明显的复制粘贴错误。
✅修正:删除button3或为其编写正确的删除逻辑。
🔴 问题4:删除后未清空输入框
添加/修改成功后清空了表单,但删除成功后没有清空对应的文本框,用户体验欠佳。
✅改进:在删除成功后调用textBox_delName.Clear()等。
🔴 问题5:年龄获取时的类型转换
代码中使用Convert.ToInt32(comboBox_age.SelectedItem),但如果用户未选择(SelectedItem为null),会抛出异常。虽然我初始化时设置了SelectedIndex=0,但严谨起见应该判断。
✅优化:
if (comboBox_age.SelectedItem == null || !int.TryParse(comboBox_age.SelectedItem.ToString(), out int age)) { MessageBox.Show("请选择有效年龄!"); return; }🔴 问题6:数据库连接未显式关闭
代码中多处写了con.Close(),但实际上using块结束时会自动释放并关闭连接,无需手动调用。手动调用也可能引发二次释放问题。
✅建议:去掉所有显式的Close(),依赖using即可。
六、总结
通过这个小项目,我们实践了WinForms的常用控件用法、ADO.NET的核心对象(Connection、Command、ExecuteNonQuery/ExecuteScalar)以及数据库的增删改基本操作。同时也暴露了代码中常见的几个坑:
忘记校验性别是否选中
忽略SQL注入风险
事件绑定错乱
资源管理可以更优雅
收获:以后写数据库交互代码,我会默认使用参数化查询+配置文件存储连接字符串+完善的输入验证。希望这篇博客也能让正在学习C#数据库编程的你少走一些弯路。
如果你有任何疑问或发现了其他bug,欢迎在评论区交流讨论!