ARTICLE DETAIL

资讯详情

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

C#学生成绩管理系统:Access数据库与ADO.NET从建表到界面绑定全指南

C#学生成绩管理系统:Access数据库与ADO.NET从建表到界面绑定全指南 简介课程作业用的C#学生成绩管理系统完整源码包面向正在学习C#编程、数据库应用开发或需要完成类似课程设计的初学者。系统以Access数据库为后端借助数据访问组件完成学生、课程、成绩三类信息的增删改查并实现界面交互、数据验证与异常处理覆盖面向对象编程、窗体控件使用、关系数据库设计、SQL命令等核心知识点同时包含学号唯一性校验、成绩范围判断等实际业务规则适合作为项目练手和对照参考。资源共90个文件以31个C#源代码文件、11个界面资源文件、4个依赖库、3个可执行程序和1个Access数据库文件为主另含解决方案与项目工程文件、图标和配置文件压缩包约3.35MB整体结构清晰可在开发环境里直接打开运行与调试。已有962人学习下载通过查看源码和数据库表结构可以快速理解业务逻辑与表关系也可在此基础上扩展报表统计、登录验证或权限管理完成自己的课程作业或期末设计。1. C#学生成绩管理系统课程作业里最该拆开看的一个完整闭环学生成绩管理系统这个题目几乎每个学 .NET 的人都在课程作业里撞见过。它看起来简单——无非是学生对课程、课程对成绩的三张表增删改查但真把一个能交差的系统写完你会发现它把 C# 语法、Access 数据库设计、ADO.NET 数据访问、Windows Forms 界面绑定、异常处理全串起来了。这个项目最好的地方在于它不像那些只练语法的小 demo它是一个有业务逻辑、有数据关联、有界面交互的完整闭环做完一遍你对一个软件到底怎么从数据库走到界面上会有实感。这套资源适合两类人一是正在做课程设计、需要一份能跑通且有注释参考的源码的学生二是工作后用不上 WinForms、想快速捡回 ADO.NET 和 Access 这套老技术栈的开发者。我不打算给你讲一堆概念直接按数据库怎么建、连接怎么写、界面怎么绑、坑在哪来拆照着做你也能把这套系统复现出来。2. 数据从哪来Access 三张表的设计与关联关系2.1 为什么课程作业选 Access 而不是 SQL Server很多人一上来就问为什么不用 SQL Server课程作业用 Access 是有现实原因的。Access 是文件型数据库一个 .accdb 文件就能跑不需要单独安装数据库服务交作业时把数据库文件一起打包就行老师打开就能验收。而 SQL Server 还得附加数据库、配账号权限光环境问题就能劝退一批人。从学习角度讲Access 支持标准的 SQL 语法SELECT、INSERT、UPDATE、DELETE 都能跑主键、外键、索引这些约束也都有。你用 Access 练会的 SQL 语句换到 SQL Server 和 MySQL 上基本是平移的。项目中用 ADO.NET 操作 Access 和操作 SQL Server 的代码结构也几乎一样只是连接串和 Provider 不同。2.2 建表语句与字段约束这个系统核心是三张表学生表、课程表、成绩表。字段设计直接参考了项目正文里提到的范式要求我建议你按下面的结构来建-- 学生表 CREATE TABLE Student ( StudentID TEXT(10) PRIMARY KEY, StudentName TEXT(20) NOT NULL, Gender TEXT(2) CHECK (Gender IN (男, 女)), BirthDate DATETIME, ClassName TEXT(30) ); -- 课程表 CREATE TABLE Course ( CourseID TEXT(10) PRIMARY KEY, CourseName TEXT(30) NOT NULL, Credit SINGLE, TeacherName TEXT(20) ); -- 成绩表通过外键关联学生和课程 CREATE TABLE Score ( StudentID TEXT(10) NOT NULL, CourseID TEXT(10) NOT NULL, Score SINGLE CHECK (Score 0 AND Score 100), PRIMARY KEY (StudentID, CourseID), FOREIGN KEY (StudentID) REFERENCES Student(StudentID), FOREIGN KEY (CourseID) REFERENCES Course(CourseID) );StudentID 和 CourseID 都用 TEXT 类型而不是数字自增主键这是 Access 场景下的常见做法。因为学号和课程编号本身就有业务含义比如 2024001 代表某个年级的学生直接用文本做主键查询时条件写起来直观也避免了自增 ID 在数据导入导出时对不上号的问题。Score 表的主键是 (StudentID, CourseID) 联合主键这保证了同一个学生同一门课只能有一条成绩记录从数据库层面杜绝了重复录入。外键约束则是为了保证你删一个学生时不会留下一堆孤立的成绩数据——虽然 Access 的外键约束执行得没有 SQL Server 那么严格但建上总比不建强。2.3 数据类型选择的三个注意点文本字段给足长度StudentID 设 TEXT(10)别用 TEXT(255) 或默认长度否则后面写验证代码时还得处理多余空格。成绩字段用 SINGLE 而不是 INTEGER成绩可能有小数点比如 85.5INTEGER 会直接截断SINGLE 够用且节省空间。日期字段用 DATETIMEAccess 里 DATE 类型实际存储也是 DATETIME你写代码取出来的是 DateTime 对象前端显示时再格式化。3. ADO.NET 连接 Access连接串、三种数据访问方式与增删改查实现3.1 连接串怎么配才不出错Access 数据库的连接串是这个项目里第一个坑。很多人直接把网上的代码抄过来结果报未找到提供程序或Microsoft.ACE.OLEDB.12.0 未注册。这是因为 Access 数据库有两种格式旧版 .mdb 用 Jet 4.0新版 .accdb 用 ACE 12.0两个的 Provider 名字不一样。// 适用于 .accdb 文件Access 2007 及以上版本 string connStr ProviderMicrosoft.ACE.OLEDB.12.0;Data Source|DataDirectory|StudentScore.accdb;Persist Security InfoFalse;; // 适用于 .mdb 文件Access 2003 及以下版本 string connStrOld ProviderMicrosoft.Jet.OLEDB.4.0;Data Source|DataDirectory|StudentScore.mdb;;|DataDirectory| 是个好用的占位符它会自动替换成程序运行目录下的数据文件夹。你把 .accdb 文件放在项目根目录下的 Data 文件夹里然后在 App.config 里配置一下代码里就不用写绝对路径了——这样作业拷到别的电脑上也能跑。连接 Access 不需要用户名密码Persist Security InfoFalse 意思是每次连接都用默认的安全机制。如果你的 Access 文件设置了数据库密码这行得改成 True并在连接串里加上 Passwordxxx。3.2 用 OleDbDataAdapter 填充 DataTable查询的正确姿势ADO.NET 操作 Access 有三件套OleDbConnection、OleDbCommand、OleDbDataAdapter。最常见的查询写法是用 DataAdapter 把结果填进 DataTable然后绑给 DataGridView 显示using System.Data.OleDb; // 1. 创建连接 using (OleDbConnection conn new OleDbConnection(connStr)) { // 2. 准备 SQL 语句 string sql SELECT StudentID, StudentName, Gender, ClassName FROM Student; // 3. 创建 DataAdapter 并填充 DataTable OleDbDataAdapter adapter new OleDbDataAdapter(sql, conn); DataTable dt new DataTable(); adapter.Fill(dt); // 4. 绑定到 DataGridView dataGridView1.DataSource dt; }这段代码逻辑很直白先创建连接对象然后准备 SQLDataAdapter 内部会自动打开连接、执行命令、把结果填充到 DataTable、再关闭连接——Fill 方法帮你把连接生命周期管理好了。你不需要手动 Open/Close这比用 OleDbCommand 手动执行要省心。如果你的查询需要参数比如按学生姓名模糊搜索别直接拼接字符串用参数化查询string sql SELECT StudentID, StudentName, Gender, ClassName FROM Student WHERE StudentName LIKE name; adapter.SelectCommand new OleDbCommand(sql, conn); adapter.SelectCommand.Parameters.AddWithValue(name, % txtKeyword.Text.Trim() %);注意 OleDb 的参数占位符是 name不是 SQL Server 的 name 或 MySQL 的 ?这是新手最容易搞混的地方。另外OleDb 不支持命名参数批量添加的某些写法你用 AddWithValue 逐个加最稳。3.3 增删改ExecuteNonQuery 与事务边界增删改操作不走 DataAdapter用 OleDbCommand 的 ExecuteNonQuery 方法。它返回受影响的行数判断执行是否成功// 新增学生 string sql INSERT INTO Student (StudentID, StudentName, Gender, BirthDate, ClassName) VALUES (?, ?, ?, ?, ?); using (OleDbConnection conn new OleDbConnection(connStr)) using (OleDbCommand cmd new OleDbCommand(sql, conn)) { // OleDb 的参数顺序严格按 SQL 中 ? 的出现顺序 cmd.Parameters.AddWithValue(p1, txtStudentID.Text.Trim()); cmd.Parameters.AddWithValue(p2, txtStudentName.Text.Trim()); cmd.Parameters.AddWithValue(p3, cmbGender.Text); cmd.Parameters.AddWithValue(p4, DateTime.Parse(dtpBirthDate.Value.ToString(yyyy-MM-dd))); cmd.Parameters.AddWithValue(p5, txtClassName.Text.Trim()); conn.Open(); int rows cmd.ExecuteNonQuery(); if (rows 0) { MessageBox.Show(学生信息添加成功); RefreshStudentList(); // 刷新 DataGridView } }这里有个关键点OleDbCommand 的参数占位符用的是 ?问号而不是 name但 AddWithValue 里你写的参数名其实会被忽略真正起作用的是参数添加的顺序。也就是说你第一个 AddWithValue 对应 SQL 里第一个 ?第二个对应第二个 ?以此类推。这是个很容易翻车的地方——顺序写错数据就像串台一样乱插。更新和删除的写法类似只是 SQL 不同string updateSql UPDATE Student SET StudentName?, Gender?, ClassName? WHERE StudentID?; string deleteSql DELETE FROM Student WHERE StudentID?;删除操作建议先判断该学生是否已有成绩记录否则会把成绩表里的关联数据一起搞出孤儿记录。一种常见做法是删除前先查 Score 表string checkSql SELECT COUNT(*) FROM Score WHERE StudentIDid; // 如果 count 0提示用户先删除成绩记录这里我用的是 id 而不是 ?是想提醒你OleDbCommand 两种写法混用没问题但不要混在同一条 SQL 里。实际项目中我一般统一用 ?因为这是 OleDb 最原生的写法碰上复杂 SQL 时不至于迷糊。3.4 DataSet 与 DataTable该用哪个项目正文里提到了 DataSet、DataTable、DataAdapter 这些组件很多初学者分不清。简单说DataTable 是一张内存表DataSet 是 DataTable 的集合可以装多张表还保持它们之间的关联关系。课程作业这个场景界面主要是单表展示用 DataTable 就够了如果你做的是那种左侧选学生、右侧自动显示该学生成绩的联动界面可以一次性把 Student 和 Score 两张表 Fill 进 DataSet通过 DataRelation 建立关联省得来回查数据库。但我的建议是这种小系统别把 DataSet 玩得太花每次操作直接查数据库反而逻辑更清楚。DataSet 的优势在大批量离线操作时才明显课程作业用不上。4. 界面层到数据层DataGridView 绑定与增删改查的完整串联4.1 界面控件职责分配Windows Forms 界面按功能分三个区域每个区域用到的控件职责很明确功能区域核心控件职责说明数据展示DataGridView展示学生/课程/成绩列表支持选中行回填数据录入TextBox、ComboBox、DateTimePicker输入学号、姓名、性别、出生日期等信息操作触发Button增、删、改、查四个操作入口性别用 ComboBox 而不是 TextBox是因为性别只有两个合法值ComboBox 限制输入范围能减少数据验证的代码量。出生日期用 DateTimePicker 而不是手写日期可以避免用户输入非法日期格式导致的异常。4.2 DataGridView 选中行回填到输入框这个交互很常见用户点列表里的某一行上面的输入框自动填上这一行的数据改完点修改按钮就更新。核心用的是 DataGridView 的 CellClick 事件private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) { if (e.RowIndex 0) return; // 点列头时不做处理 DataGridViewRow row dataGridView1.Rows[e.RowIndex]; txtStudentID.Text row.Cells[StudentID].Value.ToString(); txtStudentName.Text row.Cells[StudentName].Value.ToString(); cmbGender.Text row.Cells[Gender].Value.ToString(); dtpBirthDate.Value Convert.ToDateTime(row.Cells[BirthDate].Value); txtClassName.Text row.Cells[ClassName].Value.ToString(); }在数据绑定完成后把 DataGridView 的 SelectionMode 设为 FullRowSelect这样用户点任意单元格都能选中整行回填的数据才是完整的。否则你只取了某一个单元格的值其他文本框还是空的。4.3 一个完整的成绩录入流程成绩录入是三个表联动最典型的场景界面上选择学生、选择课程、填分数保存时往 Score 表插数据。我一般把学生和课程分别放到两个 ComboBox 里下拉选项的数据源直接查表// 加载学生下拉框 string studentSql SELECT StudentID, StudentName FROM Student; OleDbDataAdapter stuAdapter new OleDbDataAdapter(studentSql, conn); DataTable stuTable new DataTable(); stuAdapter.Fill(stuTable); cmbStudent.DataSource stuTable; cmbStudent.DisplayMember StudentName; // 下拉框显示姓名 cmbStudent.ValueMember StudentID; // 实际用的是学号关键在这里DisplayMember 和 ValueMember 必须分开设置。DisplayMember 决定用户看到什么ValueMember 决定代码拿到什么。很多初学者只设置了 DisplayMember结果取到的值变成 System.Data.DataRowView就是因为 ValueMember 没设。录入成绩时取两个 ComboBox 的 SelectedValue 再插入 Score 表string sql INSERT INTO Score (StudentID, CourseID, Score) VALUES (?, ?, ?); cmd.Parameters.AddWithValue(p1, cmbStudent.SelectedValue.ToString()); cmd.Parameters.AddWithValue(p2, cmbCourse.SelectedValue.ToString()); cmd.Parameters.AddWithValue(p3, txtScore.Text.Trim());还有一个小细节两个下拉框的 SelectedValue 在用户没选择时可能是 null先做个判空否则你会在 NULL 引用异常上多花十分钟。4.4 查询结果的联动显示按学生查成绩按学生查成绩是这套系统里查询功能里最有代表性的一段逻辑。先按学号查 Student 表拿学生信息再按学号查 Score 表和 Course 表联表查出成绩列表SELECT Score.Score, Course.CourseName, Course.Credit FROM Score INNER JOIN Course ON Score.CourseID Course.CourseID WHERE Score.StudentID ?这段 SQL 用 INNER JOIN 把成绩表和课程表关联起来查出来的结果直接绑定到 DataGridView。你问为什么不用三表 JOIN因为学生姓名已经在界面上单独显示了不必要再查一遍。课程作业里做 JOIN 是加分项说明你理解了表间关系而不只是会单表查询。5. 避坑与常见问题Access 项目里四类高频翻车现场5.1 未在本地计算机上注册 Microsoft.ACE.OLEDB.12.0 提供程序现象程序一跑连接就抛异常报错信息写着未找到 ACE.OLEDB.12.0 提供程序。原因你的电脑没装 Access Database Engine或者装了 64 位版本但项目编译成了 x86或反过来。解决去微软官网下载 AccessDatabaseEngine 安装包注意位数必须和项目编译平台一致。项目编译成 AnyCPU 时在 x64 系统上默认以 x64 运行如果你装的是 32 位驱动就会报错。最稳的做法是把项目平台目标改成 x86然后装 32 位的 ACE 驱动——兼容性最好因为 Office 默认也是 32 位。5.2 参数顺序错乱导致数据写入错误的列现象往 Student 表插入数据学号和姓名写反了或者性别跑到姓名字段里。原因OleDbCommand 的参数按 ? 占位符的顺序对应如果你把 Parameters.Add 的顺序写错数据就串了。解决写完 SQL 后数一下有几个 ? 占位符然后再数一遍你 Add 了多少个参数两者的顺序必须一一对应。每次写完我都在参数前加注释标明这个参数对应 SQL 里的哪个位置这个习惯能避免大多数串位问题。5.3 修改学生信息时提示索引或主键不能包含 Null 值现象点修改按钮后程序报错说是主键不能为空。原因你把 StudentID 列也放进了 UPDATE 语句的 SET 子句里而这个字段在主键约束下不允许为空或者你把主键值也改了导致冲突。解决UPDATE 语句只更新允许修改的字段主键字段只出现在 WHERE 子句里做定位条件。学号不想让学生改就把学号文本框设置成只读ReadOnly true从源头避免这个问题。5.4 删除有成绩的学生时没有提示事后数据对不上现象删掉一个学生后查成绩报表发现总数对不上Score 表里还留着这个学生的记录。原因删除代码没处理外键关联或者 Access 没有启用参照完整性。解决删除前先查 Score 表判断有无关联数据。如果有要么禁止删除并提示用户先清成绩要么在删除学生记录的同时级联删除成绩记录——课程作业里我建议用后者代码直观老师也容易看懂using (OleDbConnection conn new OleDbConnection(connStr)) { conn.Open(); OleDbTransaction trans conn.BeginTransaction(); try { string deleteScoreSql DELETE FROM Score WHERE StudentID?; OleDbCommand cmd1 new OleDbCommand(deleteScoreSql, conn, trans); cmd1.Parameters.AddWithValue(p1, studentId); cmd1.ExecuteNonQuery(); string deleteStudentSql DELETE FROM Student WHERE StudentID?; OleDbCommand cmd2 new OleDbCommand(deleteStudentSql, conn, trans); cmd2.Parameters.AddWithValue(p1, studentId); cmd2.ExecuteNonQuery(); trans.Commit(); } catch { trans.Rollback(); MessageBox.Show(删除失败数据已回滚); } }这里用到了事务两条 DELETE 要么都成功要么都失败不会出现删了学生还留成绩的中间状态。这个写法在课程作业里属于加分项能体现你对数据一致性的理解。5.5 Access 数据库文件被占用无法修改现象调试运行一次后再次运行程序提示数据库文件被锁定无法写入。原因上一个程序实例没有完全释放连接资源Access 文件被锁定。解决所有的 OleDbConnection 都用 using 包裹确保用完立即释放。另外检查是不是调试时程序没有完全退出任务管理器里把残留的进程结束掉。Access 单文件架构就是有这个问题不像 SQL Server 有独立的进程管理连接所以你的代码里必须手动管理连接生命周期。6. 把代码组织成能复用的小工具参数化查询与统一数据访问类做到这一步系统功能基本齐全了。但如果你想把项目的代码质量往上提一个台阶我建议做一件事把数据库操作封装到一个独立的类里而不是每次都在按钮事件里 new OleDbConnection、写重复 SQL。这个习惯在课程作业里看着是多余但它是你从写功能走向写代码的分水岭。我一般把数据访问封装成一个 ScoreDB 类类里只暴露业务方法public class ScoreDB { private static readonly string connStr ProviderMicrosoft.ACE.OLEDB.12.0;Data SourceData\StudentScore.accdb;; /// 查询学生列表支持精确学号或模糊姓名 public DataTable GetStudents(string id , string name ) { string sql SELECT StudentID, StudentName, Gender, BirthDate, ClassName FROM Student WHERE 11; if (!string.IsNullOrEmpty(id)) sql AND StudentID LIKE id; if (!string.IsNullOrEmpty(name)) sql AND StudentName LIKE name; using (OleDbConnection conn new OleDbConnection(connStr)) using (OleDbDataAdapter adapter new OleDbDataAdapter(sql, conn)) { if (!string.IsNullOrEmpty(id)) adapter.SelectCommand.Parameters.AddWithValue(id, % id %); if (!string.IsNullOrEmpty(name)) adapter.SelectCommand.Parameters.AddWithValue(name, % name %); DataTable dt new DataTable(); adapter.Fill(dt); return dt; } } /// 按学号查成绩联表返回课程名和成绩 public DataTable GetScoresByStudent(string studentId) { string sql SELECT Course.CourseName, Course.Credit, Score.Score FROM Score INNER JOIN Course ON Score.CourseID Course.CourseID WHERE Score.StudentID id; using (OleDbConnection conn new OleDbConnection(connStr)) using (OleDbDataAdapter adapter new OleDbDataAdapter(sql, conn)) { adapter.SelectCommand.Parameters.AddWithValue(id, studentId); DataTable dt new DataTable(); adapter.Fill(dt); return dt; } } }这段代码里有几个值得参考的细节。WHERE 11 是一个拼接技巧它让后面跟的 AND 子句不需要判断是不是第一段条件代码看起来整齐也不会出现 WHERE 后面直接跟 AND 的语法错误。参数化查询统一用 id、name 占位而不是 ?这是为了让 SQL 字符串可读性更高——OleDb 的 ? 占位方式在拼接动态 SQL 时容易看花眼占位在 SQL 文本里是显式的你能直接看出哪个参数对哪个字段。把数据访问封装成类之后界面层的按钮事件代码变得非常干净。一个按钮事件就两三行private void btnSearch_Click(object sender, EventArgs e) { ScoreDB db new ScoreDB(); DataTable dt db.GetStudents(txtStudentID.Text.Trim(), txtStudentName.Text.Trim()); dataGridView1.DataSource dt; }从那以后我每次写课程作业都强制自己把数据访问和界面层分开即使老师只要求一个能跑的程序。因为当你把 SQL 逻辑从界面事件里抽出来之后你会发现程序的排错路径清晰很多界面出问题去查事件数据出问题去查 ScoreDB 类不用把一个 300 行的按钮事件从头翻到尾。最后多说一句Access 和 ADO.NET 这套组合看着老但它的核心——连接管理、参数化查询、事务处理、DataTable 绑定——换到 SQL Server、MySQL 甚至 EF Core 里都是同一套思路。把这份课程作业拆透你收获的不仅是一个能交差的项目而是一条完整的数据库应用开发方法论。希望帮到你。本文还有配套的精品资源点击获取
返回列表