简介:这份源码包是一个基于C#与SQL Server数据库开发的学生选课及成绩查询管理系统,面向需要完成课程设计、毕业设计或希望学习桌面数据库应用开发的人群,可直接用来熟悉从界面设计、数据表创建到增删改查的完整流程。系统使用C#窗体作为前端,采用ADO.NET连接后台数据库,实现了学生管理、课程管理、选课记录、成绩查询与权限控制等典型功能,同时配有数据库建表脚本、运行说明以及界面预览图片,方便初学者快速理解项目结构。压缩包中共有123个文件,主要包括C#源代码、窗体资源文件、可执行程序与依赖库、数据库物理文件及SQL脚本,此外还有相关文档和配置文件,整体压缩后大小为4.15MB,便于下载和本地部署。目前已有366人浏览学习,适合作为课程设计或毕业设计的参考资料;开发者打开解决方案后,既可以查看各功能模块的代码组织方式,也能借助数据库脚本直接搭建环境,为二次开发和功能扩展提供良好基础。
1. 学生选课及成绩查询管理系统:从课设题目到一个能交差的完整方案
很多人在拿到“基于C#与SQL Server数据库的学生选课及成绩查询管理系统”这个题目时,第一反应是上网找现成源码。但真正让人头疼的往往不是业务逻辑,而是这一套东西能不能在自己机器上跑起来、能不能在答辩现场演示流畅、能不能在交给别人部署时少挨骂。这套系统本质上就是 C# 写界面、SQL Server 存数据、用增删改查把学生选课和成绩查询串起来的一个典型课设项目。文章会从建表、数据访问层、登录角色一路写到选课并发、成绩录入和源码打包后遇到的坑。适合正在做课设的本科生、准备转向.NET方向的新人,以及想快速搭一个内部选课原型的一线开发。
2. 先把架子立起来:选课系统的表结构设计与三张核心表
2.1 选课业务拆成三张表:Student、Course、SC为什么够用
选课系统的业务实体并不复杂:学生要登录、要选课、要查成绩;课程有基本信息、有容量限制;学生和课程之间是“选课”这个关系,关系上还要挂一个成绩字段。所以三张表就能覆盖全部核心场景,不需要在一开始就引入角色表、权限表、日志表这些扩展设计。
- Student:学生信息,包含学号、姓名、班级、登录密码。
- Course:课程信息,包含课程号、课程名、学分、教师、容量、已选人数。
- SC:选课关系表,记录哪个学生选了哪门课、考了多少分。
把密码直接放在 Student 表里对课设是可接受的,但要知道这在生产环境是不合格的。如果想让项目看起来更完整,可以多建一张 Teacher 表和 Admin 表,登录时分表查询,也就是极简的 RBAC。我一般建议在核心三表跑通后再加,不要一开始就把联表复杂度堆上去,否则后面调试时光是区分登录角色就会耗掉大量时间。表结构设计阶段多花十分钟想清楚字段类型,后面写 C# 会少很多“类型转换失败”的报错。
2.2 建库建表与初始数据:一份可重复执行的SQL脚本
-- ============================================= -- 选课系统建库脚本,适用于 SQL Server 2008 R2 及以上版本 -- 文件编码建议保存为 UTF-8 with BOM,避免中文乱码 -- ============================================= IF DB_ID('CourseDB') IS NULL CREATE DATABASE CourseDB; GO USE CourseDB; GO -- 学生表:登录密码用 nvarchar,课设场景可以先放明文 IF OBJECT_ID('dbo.Student', 'U') IS NULL BEGIN CREATE TABLE dbo.Student ( StudentId NVARCHAR(20) NOT NULL PRIMARY KEY, -- 学号,如 20230001 StudentName NVARCHAR(50) NOT NULL, -- 姓名 ClassName NVARCHAR(50) NULL, -- 班级 Pwd NVARCHAR(50) NOT NULL DEFAULT '123456' ); END GO -- 课程表 IF OBJECT_ID('dbo.Course', 'U') IS NULL BEGIN CREATE TABLE dbo.Course ( CourseId NVARCHAR(10) NOT NULL PRIMARY KEY, -- 课程号 CourseName NVARCHAR(50) NOT NULL, -- 课程名 Credit DECIMAL(3,1) NOT NULL DEFAULT 2, -- 学分,支持3.5这种值 Teacher NVARCHAR(50) NULL, -- 任课教师 Capacity INT NOT NULL DEFAULT 60, -- 选课容量 Selected INT NOT NULL DEFAULT 0 -- 已选人数 ); END GO -- 选课关系表:联合主键,成绩允许为空表示未录入 IF OBJECT_ID('dbo.SC', 'U') IS NULL BEGIN CREATE TABLE dbo.SC ( StudentId NVARCHAR(20) NOT NULL, CourseId NVARCHAR(10) NOT NULL, Score DECIMAL(5,1) NULL, PRIMARY KEY (StudentId, CourseId) ); END GO -- 插入少量测试数据 INSERT INTO dbo.Student (StudentId, StudentName, ClassName) VALUES ('20230001', N'张伟', N'计科2301'), ('20230002', N'李娜', N'计科2301'), ('20230003', N'王强', N'软工2302'); INSERT INTO dbo.Course (CourseId, CourseName, Credit, Teacher, Capacity) VALUES ('CS101', N'C#程序设计', 3.0, N'刘老师', 60), ('CS102', N'数据库原理', 3.0, N'陈老师', 50), ('MA101', N'高等数学', 4.0, N'赵老师', 120); GO逻辑说明:脚本里用了IF OBJECT_ID('dbo.表名', 'U') IS NULL做前置判断,执行前会检查对象是否已存在,所以脚本可以重复跑而不会报错。这在你反复建库、或者把脚本交给同学执行时非常省心。数据库名固定为 CourseDB,SQL Server 2008 R2 到 2019 都能直接执行。
参数说明:StudentId 用 NVARCHAR(20) 而不是 INT,因为学号经常以 0 开头,用整数类型会把前导零丢掉。Credit 用 DECIMAL(3,1) 而不是 FLOAT,避免浮点比较的精度问题。Pwd 默认给123456,便于开发阶段快速登录,但答辩前最好换成散列值,第 6 章会写具体做法。
2.3 增删改查之外:外键与CHECK约束让数据库兜住脏数据
很多新手喜欢把所有校验写在 C# 界面里,数据库表建成“光秃秃”的四张表,什么约束都不加。这样做的后果是:C# 端漏掉一个判断,库里就多一条脏数据。选课关系表应该加上外键和 CHECK 约束,让数据库成为第二道防线。
-- 外键约束:保证选课记录里的学生和课程真实存在 ALTER TABLE dbo.SC ADD CONSTRAINT FK_SC_Student FOREIGN KEY (StudentId) REFERENCES dbo.Student(StudentId); ALTER TABLE dbo.SC ADD CONSTRAINT FK_SC_Course FOREIGN KEY (CourseId) REFERENCES dbo.Course(CourseId); -- 课程表约束:已选人数不能为负数,且不能超过容量 ALTER TABLE dbo.Course ADD CONSTRAINT CK_Course_Selected CHECK (Selected >= 0 AND Selected <= Capacity);逻辑说明:加上外键后,往 SC 表插入一条不存在的 StudentId 会直接报错,C# 端只要捕获 SqlException 就能给用户提示。CHECK 约束保证 Selected 永远不可能超过 Capacity,哪怕 C# 端的并发判断出了问题,数据库也会拒绝更新。这个是“双保险”思路,课设答辩时很加分。
2.4 数据访问前的自检:三条SQL确认没问题再动C#
建好表后不要急着写 WinForms 界面。先打开 SSMS,手工执行三条查询,确认数据、中文、联表都没问题:
-- 验证学生登录 SELECT StudentId, StudentName FROM dbo.Student WHERE StudentId = '20230001' AND Pwd = '123456'; -- 验证选课联表查询 SELECT c.CourseId, c.CourseName, sc.Score FROM dbo.SC sc JOIN dbo.Course c ON sc.CourseId = c.CourseId WHERE sc.StudentId = '20230001'; -- 验证课程容量 SELECT CourseId, CourseName, Capacity, Selected FROM dbo.Course WHERE CourseId = 'CS101';这三条语句分别对应登录、成绩查询、课程列表三个核心功能的 SQL 骨架。能在 SSMS 里跑通,再去写 C# 代码,排错时会轻松很多——至少你知道问题不在 SQL 而在连接或参数传递上。
3. 用C#连上SQL Server:连接字符串、通用数据访问层与登录角色
3.1 连接字符串的两种写法:连实例与连mdf文件的取舍
C# 连 SQL Server 最常用的是 SqlClient,也就是 System.Data.SqlClient 命名空间。连接字符串有两类典型写法:一种是连本机或远程的 SQL Server 实例,另一种是用 AttachDbFilename 直接附加 .mdf 文件。许多开发者在本地用第二种写法跑得很顺,换台机器就因为权限或路径问题彻底连不上。我一般建议课设直接使用实例连接,部署时只要目标机器装了 SQL Server 并且创建了同名数据库即可。附加 .mdf 的方式只适合演示时临时用,不适合作为交付方案。
using System.Data; using System.Data.SqlClient; public static class DbHelper { // 1) 实例连接(推荐):Server 可写成 localhost 或你的服务器名 // private static readonly string ConnStr = // "Server=localhost;Database=CourseDB;User Id=sa;Password=123456;"; // 2) 附加mdf:配合 |DataDirectory| 使用,打包后容易出问题 // private static readonly string ConnStr = // @"Server=localhost\SQLEXPRESS;AttachDbFilename=|DataDirectory|\CourseDB.mdf; // Integrated Security=true;User Instance=true;"; // 实际建议:从 App.config 读取,部署时改配置即可 public static string GetConnectionString() { return System.Configuration.ConfigurationManager .ConnectionStrings["CourseDB"].ConnectionString; } // 执行增删改,返回受影响行数 public static int ExecuteNonQuery(string sql, params SqlParameter[] pars) { using (var conn = new SqlConnection(GetConnectionString())) using (var cmd = new SqlCommand(sql, conn)) { if (pars != null) cmd.Parameters.AddRange(pars); conn.Open(); return cmd.ExecuteNonQuery(); } } // 执行查询,返回 DataTable,可直接绑定 DataGridView public static DataTable ExecuteQuery(string sql, params SqlParameter[] pars) { using (var conn = new SqlConnection(GetConnectionString())) using (var cmd = new SqlCommand(sql, conn)) using (var da = new SqlDataAdapter(cmd)) { if (pars != null) cmd.Parameters.AddRange(pars); var dt = new DataTable(); da.Fill(dt); return dt; } } }逻辑说明:连接字符串放在 App.config 里,部署时用记事本改 Server、User Id、Password 即可,不需要重新编译。ExecuteNonQuery 负责 INSERT、UPDATE、DELETE,ExecuteQuery 负责 SELECT 并返回 DataTable,UI 层拿到 DataTable 后直接设置 DataGridView.DataSource 就能显示。using 块保证 SqlConnection 用完即释放,避免连接泄漏。
参数说明:这里所有方法都接受params SqlParameter[],强制调用方用参数化方式拼 SQL。选课、登录、成绩录入这些操作一旦用字符串拼接,输入单引号或分号就可能导致注入或语法错误。答辩老师喜欢盯着这个问,务必养成参数化习惯。
3.2 登录功能:按角色分流到不同表
登录是系统的门户。常见做法是学生、教师、管理员分表存放,登录时依次查询,命中哪个表就返回哪个角色,然后由主窗体根据角色打开不同的子界面。
public string Login(string userId, string pwd, out string role) { role = ""; // 查学生表 var dt = DbHelper.ExecuteQuery( "SELECT StudentName FROM dbo.Student WHERE StudentId=@id AND Pwd=@pwd", new SqlParameter("@id", userId), new SqlParameter("@pwd", pwd)); if (dt.Rows.Count > 0) { role = "student"; return dt.Rows[0][0].ToString(); } // 查教师表(假设已建好 Teacher 表,字段同 Student) dt = DbHelper.ExecuteQuery( "SELECT TeacherName FROM dbo.Teacher WHERE TeacherId=@id AND Pwd=@pwd", new SqlParameter("@id", userId), new SqlParameter("@pwd", pwd)); if (dt.Rows.Count > 0) { role = "teacher"; return dt.Rows[0][0].ToString(); } // 查管理员表 dt = DbHelper.ExecuteQuery( "SELECT AdminName FROM dbo.Admin WHERE AdminId=@id AND Pwd=@pwd", new SqlParameter("@id", userId), new SqlParameter("@pwd", pwd)); if (dt.Rows.Count > 0) { role = "admin"; return dt.Rows[0][0].ToString(); } return ""; }逻辑说明:登录时按顺序查三张表,命中后返回显示名称和角色值。主窗体拿到 role 之后,决定显示学生主界面还是教师主界面。参数化的 @id 和 @pwd 是底线,不要写成WHERE StudentId='" + userId + "',这是 C# 入门阶段最容易犯的错误之一。
3.3 为什么不在连接字符串里写死sa密码
项目交付时,最尴尬的场面是数据库在另一台机器上起不来。如果 sa 密码写死在代码里,换一台机器就得改代码重新编译;如果连接字符串放在 App.config 里,对方用记事本打开就能修改,风险最低。集成安全(Integrated Security=SSPI)在开发机上非常好用,但部署到没有域环境、当前 Windows 账户没有被加入 SQL Server 登录项的机器上时,会遇到登录失败。所以在部署文档里写清两种认证方式的切换步骤,比帮对方远程调试更高效。开发者自己要知道的是:用 sa 账号时必须先确认 SQL Server 的“混合认证模式”已开启,否则就算密码正确也连不进去。
4. 从登录到业务:选课、退课、成绩录入与查询的C#实现
4.1 学生端:加载可选课程列表与我的成绩
学生登录后的核心诉求很直接:看看这学期能选哪些课、选了的课考了多少分。可选课程列表需要过滤掉“已满”的和“自己已经选过”的课程,成绩查询则是简单的联表查询。
public DataTable GetAvailableCourses(string studentId) { string sql = @" SELECT c.CourseId, c.CourseName, c.Credit, c.Teacher, c.Capacity, c.Selected FROM dbo.Course c WHERE c.Selected < c.Capacity AND c.CourseId NOT IN ( SELECT sc.CourseId FROM dbo.SC sc WHERE sc.StudentId = @sid )"; return DbHelper.ExecuteQuery(sql, new SqlParameter("@sid", studentId)); } public DataTable GetMyScores(string studentId) { string sql = @" SELECT c.CourseName, c.Credit, sc.Score, c.Teacher FROM dbo.SC sc JOIN dbo.Course c ON sc.CourseId = c.CourseId WHERE sc.StudentId = @sid"; return DbHelper.ExecuteQuery(sql, new SqlParameter("@sid", studentId)); }逻辑说明:GetAvailableCourses 用Selected < Capacity过滤掉满员课程,用NOT IN过滤掉已选课程,两个条件缺一不可。GetMyScores 返回的是成绩视图,Score 为 NULL 表示还未出分。把这两段 SQL 拿到 SSMS 里验证过再贴进代码,能省不少调试时间。
4.2 选课与退课:一个UPDATE加一个INSERT的事务边界
选课的核心不是一个简单的 INSERT,而是“先判断容量、再更新已选人数、最后插入选课记录”三步。这三步如果不用事务包起来,两个学生同时抢最后一门课时,就会出现超卖:容量 60 的课,第 61 个人也能选上。事务加条件更新是最稳的解法。
public string SelectCourse(string studentId, string courseId) { using (var conn = new SqlConnection(DbHelper.GetConnectionString())) { conn.Open(); using (var tx = conn.BeginTransaction()) { try { // 第一步:条件更新,只有未满才成功 var cmd1 = new SqlCommand(@" UPDATE dbo.Course SET Selected = Selected + 1 WHERE CourseId = @cid AND Selected < Capacity", conn, tx); cmd1.Parameters.AddWithValue("@cid", courseId); int rows = cmd1.ExecuteNonQuery(); if (rows == 0) { tx.Rollback(); return "该课程已满或不存在"; } // 第二步:插入选课记录 var cmd2 = new SqlCommand(@" INSERT INTO dbo.SC (StudentId, CourseId) VALUES (@sid, @cid)", conn, tx); cmd2.Parameters.AddWithValue("@sid", studentId); cmd2.Parameters.AddWithValue("@cid", courseId); cmd2.ExecuteNonQuery(); tx.Commit(); return "选课成功"; } catch { tx.Rollback(); return "选课失败,可能已选过该课程"; } } } }逻辑说明:这段代码的关键在于把 UPDATE 当成原子判断来用。UPDATE ... WHERE CourseId = @cid AND Selected < Capacity执行后,如果影响行数是 0,说明容量已满,直接回滚;如果是 1,说明当前连接成功占到了一个名额,再插入选课记录。两个并发请求同时执行这条 UPDATE,数据库行锁会保证只有一个成功。这比“先 SELECT 再 UPDATE”可靠得多。
参数说明:AddWithValue 在并发不高和字段类型明确时可以用,但在索引列上有可能因为类型推断为 NVARCHAR(1) 而引发隐式转换。严格做法是new SqlParameter("@cid", SqlDbType.NVarChar, 10),课设阶段不展开性能问题,但要知道这个坑。
4.3 教师端:录入成绩与批量更新
教师选择一门课程后,界面显示选课学生列表,教师可以直接在 DataGridView 里输入成绩,然后点保存批量更新。更新逻辑要用事务包起来,避免一半成功一半失败。
public void SaveScores(DataTable dt) { using (var conn = new SqlConnection(DbHelper.GetConnectionString())) { conn.Open(); using (var tx = conn.BeginTransaction()) { try { var cmd = new SqlCommand(@" UPDATE dbo.SC SET Score = @score WHERE StudentId = @sid AND CourseId = @cid", conn, tx); var p1 = new SqlParameter("@score", SqlDbType.Decimal); var p2 = new SqlParameter("@sid", SqlDbType.NVarChar, 20); var p3 = new SqlParameter("@cid", SqlDbType.NVarChar, 10); cmd.Parameters.AddRange(new[] { p1, p2, p3 }); foreach (DataRow row in dt.Rows) { p1.Value = row["Score"] == DBNull.Value ? (object)DBNull.Value : row["Score"]; p2.Value = row["StudentId"].ToString(); p3.Value = row["CourseId"].ToString(); cmd.ExecuteNonQuery(); } tx.Commit(); MessageBox.Show("成绩保存成功"); } catch { tx.Rollback(); throw; } } } }逻辑说明:这里没有用 SqlDataAdapter 的 Update 方法,而是手动遍历 DataRow 逐条执行 UPDATE。这样事务边界最清晰,哪一行失败都能整体回滚。选课系统的成绩录入量一般不大,这种写法的性能完全够用。如果以后要支持几百人批量录入,再改用临时表或 TableAdapter 也不迟。
4.4 管理员端:课程维护与选课人数超限
管理员端的核心功能是课程维护:新增课程、调整容量、查看每门课的选课名单。新增课程时,Capacity 不能为 0 且应为正数;调整容量时,新容量不能小于当前已选人数,否则会制造出Selected > Capacity的脏数据。界面上要校验,数据库里也要有 CHECK 约束兜底。
if (newCapacity < currentSelected) { MessageBox.Show("新容量不能小于当前已选人数"); return; }这段逻辑放在界面上是第一道防线。配合 2.3 里的 CHECK 约束,即使 C# 层漏检,数据库也会在 UPDATE 时报错。管理员界面只需要简单调用 DbHelper.ExecuteNonQuery 执行 INSERT 或 UPDATE,不需要额外复杂逻辑。
5. 避坑与排查清单:从连不上库到源码打包的6个典型问题
5.1 连接字符串没问题却连不上:先看SQL Server实例是否在运行
现象:代码在同学电脑上运行正常,自己机器上一直报provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server。 原因:SQL Server 服务没有启动,或者实例名写错。最常见的错误是把服务器名写成 localhost,但本机实际安装的实例是.\SQLEXPRESS。 解决:先打开“服务”管理器,确认 SQL Server (SQLEXPRESS) 服务正在运行;再用 SSMS 以localhost\SQLEXPRESS登录一次,确认能进;最后把连接字符串 Server 改成localhost\SQLEXPRESS。如果装的是默认实例,Server 写localhost或.都可以。这条血泪经验排在第一位,因为大多数连不上问题根本不是代码问题。
5.2 集成认证在部署机上报登录失败
现象:开发机用Integrated Security=True一切正常,拷贝到另一台机器后报Cannot open database "CourseDB" requested by the login。 原因:目标机器上的 SQL Server 没有为当前 Windows 账户创建登录名,或者数据库权限没有授予给该账户。 解决:在目标机器上用 sa 登录执行一条授权脚本:
CREATE LOGIN [MACHINE\Username] FROM WINDOWS; USE CourseDB; CREATE USER [MACHINE\Username] FOR LOGIN [MACHINE\Username]; EXEC sp_addrolemember 'db_owner', 'MACHINE\Username';如果不想折腾 Windows 账户,就直接在连接字符串里改用 SQL Server 身份验证,并保证 sa 登录已启用。两种方式二选一,不要混合使用。
5.3 中文乱码:脚本文件编码与N前缀缺一不可
现象:执行建表脚本后,表里的中文变成??。 原因:SQL 脚本文件编码不是 UTF-8 with BOM,且 INSERT 语句中的中文字符串没有加 N 前缀。SSMS 按 ANSI 代码页保存脚本时,中文会被转换成乱码。 解决:在 SSMS 里把脚本另存为“UTF-8 with signature”编码;编写 INSERT 时,凡是插入到 NVARCHAR 字段的中文都写成N'中文'。表结构设计阶段就把字符串列定为 NVARCHAR,从根上避免编码问题。
5.4 选课超卖:先查再插一定翻车
现象:学生同时提交选课请求时,实际上选人数超过课程容量。 原因:用了“先 SELECT 判断容量、再 UPDATE、再 INSERT”的写法,两个请求同时读到 Selected=59,都认为自己还有名额,结果都执行了更新。 解决:采用 4.2 里的条件更新写法,用UPDATE ... WHERE Selected < Capacity让数据库行锁仲裁;同时在 Course 表上保留 CHECK 约束作为兜底。如果能接受存储过程方案,直接使用存储过程实现同样逻辑。
5.5 源码打包交付后数据库路径失效
现象:把整个项目文件夹拷给同学,对方打开后连接数据库报找不到文件。 原因:开发时用了AttachDbFilename=|DataDirectory|\CourseDB.mdf写法,DataDirectory 默认指向 bin\Debug 目录,打包时 .mdf 文件没有被复制到输出目录,或者目标机器上的 SQL Server Express 权限不足。 解决:第一种做法是把 .mdf 文件属性设置为 Content + Copy if newer,确保生成时包含进输出目录;第二种做法是改成实例连接,把建库脚本一并放进 deliverables。我更建议用第二种,因为验收老师通常希望看到你会用 SSMS 建库,而不是只提供一个黑匣子文件。
5.6 发布后的exe双击无反应
现象:部署到另一台机器后,双击 exe 没有任何反应,事件查看器提示版本或框架问题。 原因:目标机器只安装了 .NET Framework 4.0,而项目目标框架是 4.7.2 或更高。 解决:在项目属性里把目标框架降到 4.5 或 4.0,前提是你用到的 API 都兼容;或者写明白交付文档,说明需要安装对应版本的 .NET Framework。WinForms 项目目标框架选 4.5 是一个稳妥的兼容水位,选课系统用到的 API 基本不会超出这个范围。
6. 进阶验收:存储过程、密码散列与演示时的数据准备
6.1 把选课逻辑收进存储过程
把 4.2 的事务代码搬进 SQL Server 存储过程,代码会精简很多,也方便在 SSMS 里直接演示:
CREATE PROCEDURE usp_SelectCourse @sid NVARCHAR(20), @cid NVARCHAR(10) AS BEGIN SET NOCOUNT ON; BEGIN TRANSACTION; UPDATE dbo.Course SET Selected = Selected + 1 WHERE CourseId = @cid AND Selected < Capacity; IF @@ROWCOUNT = 0 BEGIN ROLLBACK; RETURN -1; -- 满员或课程不存在 END INSERT INTO dbo.SC (StudentId, CourseId) VALUES (@sid, @cid); COMMIT; RETURN 0; END逻辑说明:存储过程的好处是业务规则集中在一处,C# 端只需要调用存储过程名并传入两个参数。答辩时如果老师问“怎么防止超选?”,直接打开存储过程指给老师看,比翻一整段 C# 事务代码更清晰。
6.2 密码至少换成散列
答辩时最容易被挑战的点是“数据库里存明文密码”。花一点时间用 SHA256 散列替换 Pwd 字段存储,C# 端加密后再入库即可。开发阶段保留123456明文可以方便测试,但交付前建议把存储逻辑改成散列版本,至少在文档里说明这是待优化项。
6.3 演示用的三组数据准备
演示前务必在库里造好一组有完整生命周期的数据:一个学生账号、三个班级名额分别剩 0、5、10 的课程,以及一条已录入成绩的记录。这样演示时可以直接展示“选课成功后名额减少”“再次点选同一门课给出提示”“成绩查询显示已录分数”三个动作。数据的准备时间不超过十分钟,但能让演示流程不冷场。
我的经验是,这类课设项目真正决定成败的不是代码量,而是数据库在不在、能不能当场跑起来、有没有一组能讲出故事的数据。源码打包交付前,我会逐一检查连接字符串的配置项、确认建库脚本放在压缩包里最显眼的位置,再手动执行一遍建库和登录流程。这套习惯帮我避免过太多次现场翻车,希望帮到你。
本文还有配套的精品资源,点击获取