☰
C# WinForms教务系统实战:ADO.NET+SQL Server LocalDB完整实现
2026/10/17 7:00:48 网站建设 项目流程

简介:这是一套基于C# Windows窗体与SQL Server开发的教务管理类综合实践项目,面向计算机专业本科生及.NET初学者,适用于课程设计、大作业或毕业设计参考。系统采用典型双角色架构(学生+管理员),完整覆盖学生信息维护、课程开设、成绩录入与统计等核心教务流程,具备良好的模块划分与可扩展性,有助于理解B/S之外的桌面端管理系统开发范式。资源包共210个文件,含44个C#业务逻辑文件、88张界面截图与图标(png/jpg)、19个本地化资源文件(resources/resx)及1个SQL建库脚本,辅以sln工程文件、配置文件与可执行exe,整体10.61MB,结构清晰便于调试与二次开发。目前已有473人学习下载,读者可直接运行体验完整功能,获取从数据库设计、WinForm界面布局、用户权限控制到成绩统计报表的全流程实现细节,是掌握C#桌面应用开发的高实用性参考案例。

1. 这不是又一个“学生管理系统”Demo:它把教务三大核心场景——信息管理、成绩录入、选课控制——压进一个 WinForms 工程里,跑通即用,改两行就能对接真实数据库

某高校实验室去年让大三学生用 C# 做教务系统大作业,结果交上来的 83 份里,72 份卡在「登录后点学生成绩页就崩」,11 份连 DataGridView 绑定都靠复制粘贴硬凑。而这个项目,从解压到双击运行.exe,全程不到 90 秒;它不玩 WPF 动画、不套 MVVM 框架、不塞 Entity Framework Code-First 的玄学迁移,就用最朴实的 WinForms + ADO.NET + SQL Server LocalDB,把学生档案增删改查、课程表动态加载、成绩批量导入导出、选课冲突实时校验这四块硬骨头全啃下来了。适合两类人:一是赶毕设 deadline 的同学,拿来改改学院名、加个专业字段,三天内能过答辩;二是想补 Windows 桌面开发实战的开发者,它没藏黑匣子——连接字符串在哪、SQL 怎么拼、事务怎么回滚、UI 线程怎么防假死,全摊开在 .cs 文件里。你不需要懂 DDD 或 Clean Architecture,但得会看SqlConnection.Open()报什么错。


2. 项目结构与技术栈:WinForms 是壳,ADO.NET 是筋,SQL Server LocalDB 是骨

2.1 目录结构解析:五个核心文件夹撑起完整业务闭环

解压后你会看到标准的 Visual Studio 解决方案结构:

StudentManagementSystem/ ├── StudentManagementSystem.sln ├── StudentManagementSystem/ # 主项目(Windows Forms App .NET Framework 4.7.2) │ ├── Properties/ │ ├── obj/ │ ├── bin/ │ ├── FormMain.cs # 主窗体:TabControl 切换「学生」「课程」「成绩」「选课」四大模块 │ ├── Forms/ # 子窗体目录 │ │ ├── FrmStudentList.cs # 学生列表(含搜索、分页、右键编辑) │ │ ├── FrmCourseList.cs # 课程列表(支持按学期筛选) │ │ ├── FrmScoreInput.cs # 成绩录入窗体(带课程/学生下拉联动) │ │ └── FrmElective.cs # 选课主界面(含已选/可选课程双列表+拖拽选课) │ ├── Models/ # 纯数据模型(无业务逻辑) │ │ ├── Student.cs # public string StudentID, Name, Gender, ClassID... │ │ ├── Course.cs # public string CourseID, CourseName, Credit, Semester... │ │ └── Score.cs # public string StudentID, string CourseID, decimal Score... │ ├── DAL/ # 数据访问层(关键!所有 SQL 都在这) │ │ ├── DBHelper.cs # 封装 SqlConnection、SqlCommand、SqlDataAdapter,含 GetConnection() 和 ExecuteNonQuery() │ │ ├── StudentDAL.cs # CRUD 方法:GetAllStudents(), AddStudent(), UpdateStudent()... │ │ ├── CourseDAL.cs # 同上,含 GetCoursesBySemester(string semester) │ │ ├── ScoreDAL.cs # 含 GetScoresByStudentID(string id), ImportFromExcel(string path) │ │ └── ElectiveDAL.cs # 核心:CheckConflict(string studentID, string courseID) + Enroll() │ ├── Utils/ # 工具类 │ │ ├── ExcelHelper.cs # NPOI 实现 Excel 导入导出(非 Office Interop,免装 Excel) │ │ └── ValidationHelper.cs # 学号格式校验、成绩范围检查(0~100)、空值拦截 │ └── Program.cs # Main 入口,含 Application.EnableVisualStyles() └── Database/ # 内置数据库脚本 └── InitDB.sql # 创建 Student/Course/Score/Elective 四张表 + 外键约束 + 示例数据

提示:DAL/目录是整个项目的命脉。它没用 ORM,所有 SQL 都是手写的参数化查询(如"SELECT * FROM Student WHERE ClassID = @classID"),这意味着你改一个字段,只需动 Model 类 + DAL 方法 + UI 绑定控件三处,没有映射配置要同步,也没有延迟加载陷阱。

2.2 技术栈选择逻辑:为什么不用 EF Core?为什么坚持 WinForms?

  • ADO.NET 而非 Entity Framework:
    毕设场景下,EF 的DbContext生命周期管理、导航属性懒加载、LINQ to SQL 翻译失败等问题,会让新手在「查学生成绩时连带查出整个院系树」这种低级错误上卡一整天。而本项目 DAL 层每个方法职责单一,GetScoresByStudentID()只执行一条SELECT ... FROM Score WHERE StudentID=@id,返回List<Score>,调用方清清楚楚知道发了几条 SQL、查了哪些字段。调试时直接在DBHelper.cs的ExecuteQuery()里打断点,SQL 字符串和参数值一目了然。

  • WinForms 而非 WPF:
    WPF 的 XAML 绑定、依赖属性、命令模式对初学者是陡峭的学习曲线。而本项目用BindingSource+DataGridView.DataSource实现数据绑定,FrmStudentList.cs中仅需三行:

    private BindingSource studentBindingSource = new BindingSource(); // ... 初始化后 studentBindingSource.DataSource = studentDAL.GetAllStudents(); dataGridView1.DataSource = studentBindingSource;

    修改数据后调用studentBindingSource.EndEdit()即可触发底层DataTable更新,再由UpdateCommand同步到数据库。没有INotifyPropertyChanged手动实现,没有ObservableCollection线程安全问题。

  • SQL Server LocalDB 而非 SQLite:
    LocalDB 是 SQL Server 的轻量版,完全兼容 T-SQL 语法(比如MERGE INTO用于成绩批量更新),且 Visual Studio 2019+ 自带,无需额外安装服务。InitDB.sql脚本里建的外键(如Elective.StudentID → Student.StudentID)在 LocalDB 中能真正生效,避免 SQLite 因默认关闭外键导致的脏数据。

2.3 数据库设计要点:四张表如何支撑选课冲突检测

Database/InitDB.sql定义了最小可行关系模型:

-- 学生表(主键 StudentID) CREATE TABLE Student ( StudentID VARCHAR(12) PRIMARY KEY, Name NVARCHAR(20) NOT NULL, Gender CHAR(1), ClassID VARCHAR(10), EnrollmentYear INT ); -- 课程表(主键 CourseID) CREATE TABLE Course ( CourseID VARCHAR(10) PRIMARY KEY, CourseName NVARCHAR(50) NOT NULL, Credit TINYINT, Semester VARCHAR(10) -- 如 '2023-2024-1' ); -- 成绩表(联合主键 StudentID+CourseID) CREATE TABLE Score ( StudentID VARCHAR(12) NOT NULL, CourseID VARCHAR(10) NOT NULL, Score DECIMAL(4,1) CHECK (Score BETWEEN 0 AND 100), PRIMARY KEY (StudentID, CourseID), FOREIGN KEY (StudentID) REFERENCES Student(StudentID), FOREIGN KEY (CourseID) REFERENCES Course(CourseID) ); -- 选课表(同成绩表结构,但允许预选未出成绩) CREATE TABLE Elective ( StudentID VARCHAR(12) NOT NULL, CourseID VARCHAR(10) NOT NULL, Status TINYINT DEFAULT 0, -- 0=待确认, 1=已选中, 2=已退选 PRIMARY KEY (StudentID, CourseID), FOREIGN KEY (StudentID) REFERENCES Student(StudentID), FOREIGN KEY (CourseID) REFERENCES Course(CourseID) );

关键设计在于Elective表的Status字段和Score表的分离:学生先在Elective中预选(Status=0),教师审核后置为 1,期末录入成绩时才写入Score表。这避免了「成绩为空却显示在成绩单」的逻辑漏洞。而冲突检测(如学生同一时段选两门课)虽未在 DB 层用 CHECK 约束实现(因涉及课程时间字段,本项目暂未包含),但在ElectiveDAL.CheckConflict()中通过 SQL 查询SELECT COUNT(*) FROM Elective e1 JOIN Course c1 ON e1.CourseID=c1.CourseID ...完成,代码可读性远高于触发器。


3. 核心模块拆解:从登录到选课,一个请求的完整链路

3.1 登录验证:不存密码明文,用 SQL 参数化防注入

FormMain.cs的登录按钮事件调用LoginDAL.ValidateUser():

// DAL/LoginDAL.cs public static bool ValidateUser(string username, string password) { string sql = "SELECT COUNT(*) FROM AdminUser WHERE Username = @username AND Password = @password"; SqlParameter[] parameters = { new SqlParameter("@username", username), new SqlParameter("@password", password) // 注意:此处应为哈希值,见下文 }; object result = DBHelper.ExecuteScalar(sql, parameters); return Convert.ToInt32(result) > 0; }

注意:项目默认账号密码为admin/123456,但密码是明文存储(InitDB.sql中INSERT INTO AdminUser VALUES ('admin', '123456'))。生产环境必须替换为哈希:在LoginDAL.ValidateUser()中,将password参数改为HashPassword(password),并用System.Security.Cryptography.SHA256生成哈希值存入数据库。修改后InitDB.sql中的密码字段需更新为'e10adc3949ba59abbe56e057f20f883e'("123456" 的 MD5,仅作演示,实际请用 SHA256)。

3.2 学生列表分页:用 SQL Server OFFSET-FETCH 实现真分页

FrmStudentList.cs的LoadStudents(int page, int pageSize)方法:

// DAL/StudentDAL.cs public List<Student> GetStudentsPaged(int page, int pageSize, string keyword = "") { string sql = @"SELECT StudentID, Name, Gender, ClassID, EnrollmentYear FROM Student WHERE (@keyword = '' OR Name LIKE '%' + @keyword + '%') ORDER BY StudentID OFFSET @offset ROWS FETCH NEXT @pageSize ROWS ONLY"; SqlParameter[] parameters = { new SqlParameter("@keyword", keyword ?? ""), new SqlParameter("@offset", (page - 1) * pageSize), new SqlParameter("@pageSize", pageSize) }; DataTable dt = DBHelper.ExecuteQuery(sql, parameters); return DataTableToList(dt); // 将 DataTable 映射为 List<Student> }

关键点:OFFSET-FETCH是 SQL Server 2012+ 的原生分页语法,比ROW_NUMBER() OVER()更简洁高效。@offset计算为(page-1)*pageSize,避免前端传错值导致负数偏移。若你用的是旧版 SQL Server(<2012),需替换为ROW_NUMBER()方案,代码量增加 5 行,但逻辑不变。

3.3 成绩批量导入:NPOI 解析 Excel,事务保证原子性

FrmScoreInput.cs的「导入 Excel」按钮触发ScoreDAL.ImportFromExcel():

// DAL/ScoreDAL.cs public bool ImportFromExcel(string filePath) { try { using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) using (var workbook = new XSSFWorkbook(fs)) // 支持 .xlsx using (var connection = DBHelper.GetConnection()) { connection.Open(); using (var transaction = connection.BeginTransaction()) { var sheet = workbook.GetSheetAt(0); for (int i = 1; i <= sheet.LastRowNum; i++) // 跳过标题行 { var row = sheet.GetRow(i); if (row == null) continue; string studentID = row.GetCell(0)?.ToString()?.Trim(); string courseID = row.GetCell(1)?.ToString()?.Trim(); decimal score = Convert.ToDecimal(row.GetCell(2)?.ToString() ?? "0"); // 校验学号/课程是否存在 if (!StudentDAL.Exists(studentID) || !CourseDAL.Exists(courseID)) throw new Exception($"第{i+1}行数据异常:学号{studentID}或课程{courseID}不存在"); string sql = "MERGE INTO Score AS target " + "USING (SELECT @studentID as StudentID, @courseID as CourseID) AS source " + "ON target.StudentID = source.StudentID AND target.CourseID = source.CourseID " + "WHEN MATCHED THEN UPDATE SET Score = @score " + "WHEN NOT MATCHED THEN INSERT (StudentID, CourseID, Score) VALUES (@studentID, @courseID, @score);"; var parameters = new SqlParameter[]{ new SqlParameter("@studentID", studentID), new SqlParameter("@courseID", courseID), new SqlParameter("@score", score) }; DBHelper.ExecuteNonQuery(sql, parameters, transaction); } transaction.Commit(); // 全部成功才提交 return true; } } } catch (Exception ex) { MessageBox.Show($"导入失败:{ex.Message}"); return false; } }

逻辑说明:

  • 使用XSSFWorkbook(而非HSSFWorkbook)支持.xlsx格式,避免 Excel 2007+ 兼容问题;
  • MERGE INTO语句实现「存在则更新,不存在则插入」,比先SELECT再INSERT/UPDATE减少一次数据库往返;
  • transaction.Commit()在循环结束后调用,任一单元格校验失败(如学号不存在)都会触发throw,事务自动回滚,保证数据一致性;
  • Excel 第一列必须为学号(StudentID),第二列为课程号(CourseID),第三列为成绩(Score),顺序不可错。

3.4 选课冲突检测:双列表拖拽 + 实时 SQL 校验

FrmElective.cs的核心交互:左侧lstAvailableCourses(可选课程),右侧lstEnrolledCourses(已选课程),支持鼠标拖拽添加。

// FrmElective.cs private void lstAvailableCourses_MouseDown(object sender, MouseEventArgs e) { if (lstAvailableCourses.SelectedItem != null) { string courseID = ((Course)lstAvailableCourses.SelectedItem).CourseID; string studentID = currentStudentID; // 当前登录学生ID // 调用 DAL 检测冲突 if (ElectiveDAL.CheckConflict(studentID, courseID)) { MessageBox.Show("选课冲突:您已选择该课程,或该课程时间与其他已选课程重叠。"); return; } // 无冲突则加入已选列表 lstEnrolledCourses.Items.Add(lstAvailableCourses.SelectedItem); ElectiveDAL.Enroll(studentID, courseID); // 插入 Elective 表 } } // DAL/ElectiveDAL.cs public static bool CheckConflict(string studentID, string courseID) { // 简单版:检查是否已选同一门课 string sql = "SELECT COUNT(*) FROM Elective WHERE StudentID = @studentID AND CourseID = @courseID AND Status = 1"; SqlParameter[] parameters = { new SqlParameter("@studentID", studentID), new SqlParameter("@courseID", courseID) }; int count = Convert.ToInt32(DBHelper.ExecuteScalar(sql, parameters)); return count > 0; }

当前CheckConflict()仅实现「同一课程不可重复选择」的基础校验。若需扩展为「时间冲突」,需在Course表中增加StartTime/EndTime/Weekday字段,并在 SQL 中关联查询SELECT COUNT(*) FROM Elective e JOIN Course c ON e.CourseID=c.CourseID WHERE e.StudentID=@sid AND c.Weekday=@wd AND @time BETWEEN c.StartTime AND c.EndTime。本项目留出扩展接口,你只需改Course.cs加字段、改InitDB.sql加列、改CheckConflict()的 SQL,三处即可上线。


4. 运行与部署:从零配置到双击运行的完整路径

4.1 开发环境准备:VS2019+、LocalDB、NPOI NuGet 包

  1. 安装 Visual Studio 2019 或更高版本(社区版免费):
    安装时勾选「.NET 桌面开发」工作负载,确保包含 .NET Framework 4.7.2 SDK。

  2. 启用 SQL Server LocalDB:
    VS2019 默认自带 LocalDB。打开「SQL Server 对象资源管理器」→ 右键「SQL Server」→ 「添加 SQL Server」→ 服务器名称填(localdb)\mssqllocaldb→ 点确定。若提示未找到,以管理员身份运行命令提示符,执行:

    sqllocaldb start mssqllocaldb
  3. 还原数据库:

    • 打开 VS 的「SQL Server 对象资源管理器」;
    • 展开(localdb)\mssqllocaldb→ 右键「数据库」→ 「新建数据库」,命名为StudentDB;
    • 右键新建的StudentDB→ 「新建查询」,粘贴Database/InitDB.sql全文,点击「执行」。
  4. 安装 NPOI NuGet 包(Excel 支持):
    在解决方案资源管理器中右键项目 → 「管理 NuGet 包」→ 切换到「浏览」→ 搜索NPOI→ 选择NPOI(非NPOI.Core)→ 安装。版本建议2.5.5(兼容 .NET Framework)。

提示:若你本地已装有完整版 SQL Server(如 Express 或 Developer),可将连接字符串中的(localdb)\mssqllocaldb替换为localhost\SQLEXPRESS,其余代码无需改动。

4.2 连接字符串配置:在 app.config 中修改数据库地址

项目根目录下的app.config文件中,<connectionStrings>节点定义了数据库连接:

<configuration> <connectionStrings> <add name="DefaultConnection" connectionString="Data Source=(localdb)\mssqllocaldb;Initial Catalog=StudentDB;Integrated Security=True;" providerName="System.Data.SqlClient" /> </connectionStrings> </configuration>
  • 若你用的是 SQL Server Express,改为:
    connectionString="Data Source=localhost\SQLEXPRESS;Initial Catalog=StudentDB;Integrated Security=True;"

  • 若需 SQL Server 账户密码登录(非 Windows 集成认证),改为:
    connectionString="Data Source=(localdb)\mssqllocaldb;Initial Catalog=StudentDB;User ID=sa;Password=your_password;"

注意:Integrated Security=True表示使用当前 Windows 用户登录,LocalDB 默认支持;若改用 SQL 账户,需在 SQL Server 中启用混合模式认证,并为sa用户设置密码。

4.3 编译与运行:三步生成可执行文件

  1. 在 VS 中打开StudentManagementSystem.sln;
  2. 右键项目 → 「属性」→ 「应用程序」选项卡 → 确认「目标框架」为.NET Framework 4.7.2;
  3. 按Ctrl+F5(不调试启动):
    • 首次运行会弹出登录框,输入admin/123456;
    • 进入主界面后,点击 Tab 页切换模块,所有功能即刻可用。

生成的可执行文件位于StudentManagementSystem\bin\Debug\StudentManagementSystem.exe,可直接拷贝到其他装有 .NET Framework 4.7.2 的 Windows 机器上运行(无需安装 VS)。

4.4 常见问题排查:五条血泪经验总结

现象 1:登录时报错 “无法打开登录所请求的数据库”

原因:app.config中Initial Catalog(数据库名)与 SQL Server 中实际创建的数据库名不一致,或数据库未创建。
解决:打开 SQL Server 对象资源管理器,确认(localdb)\mssqllocaldb下存在名为StudentDB的数据库;若不存在,按 4.1 步骤手动创建并执行InitDB.sql。

现象 2:学生列表空白,DataGridView 无数据显示

原因:StudentDAL.GetAllStudents()返回空集合,常见于DBHelper.cs中GetConnection()方法未正确读取app.config的连接字符串。
解决:在DBHelper.GetConnection()中打断点,检查ConfigurationManager.ConnectionStrings["DefaultConnection"]是否为null;若为null,确认app.config文件已添加到项目中,且「复制到输出目录」属性为「始终复制」。

现象 3:导入 Excel 时提示 “索引超出了数组界限”

原因:Excel 文件中某行列数少于 3 列(学号、课程号、成绩),row.GetCell(2)返回null,Convert.ToDecimal(null)抛异常。
解决:在ImportFromExcel()循环内增加空值判断:

if (row.GetCell(0) == null || row.GetCell(1) == null || row.GetCell(2) == null) continue; // 跳过不完整行
现象 4:选课后右侧列表显示,但数据库Elective表无记录

原因:ElectiveDAL.Enroll()方法中ExecuteNonQuery()未传入事务对象,或connection未开启。
解决:检查Enroll()方法是否调用DBHelper.ExecuteNonQuery(sql, parameters, transaction)(带 transaction 参数);若无,则改为DBHelper.ExecuteNonQuery(sql, parameters),并确认connection.State == ConnectionState.Open。

现象 5:修改学生信息后点击「保存」无反应,也不报错

原因:FrmStudentList.cs中dataGridView1_CellEndEdit事件未绑定,或绑定的事件处理程序中漏掉studentBindingSource.EndEdit()。
解决:在窗体设计器中双击dataGridView1→ 自动生成事件,确保方法体包含:

private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e) { studentBindingSource.EndEdit(); // 必须有!否则 DataTable 不更新 // 后续 SaveChanges() 逻辑... }

5. 二次开发实战:从「能跑」到「能改」的三个关键改造点

5.1 新增「专业」字段:五步完成全链路打通

这是毕设中最常被要求的扩展。我们以在学生信息中增加「专业(Major)」字段为例,演示如何从数据库到 UI 全流程修改:

层级文件修改点说明
数据库Database/InitDB.sql在CREATE TABLE Student中添加Major NVARCHAR(30)列;在INSERT INTO Student示例数据中补全该字段值字段类型用NVARCHAR支持中文专业名,如「计算机科学与技术」
模型Models/Student.cs添加public string Major { get; set; }属性属性名必须与数据库列名一致(忽略大小写),否则DataTableToList()映射失败
数据访问DAL/StudentDAL.cs在GetAllStudents()的 SQL 中添加Major字段;在AddStudent()的INSERT语句中加入Major列和参数所有 CRUD 方法的 SQL 都需同步更新,漏一处会导致新增/修改失败
UI 绑定FrmStudentList.cs在dataGridView1列集合中右键 → 「编辑列」→ 添加新列,DataPropertyName设为Major;在新增学生窗体中添加TextBox txtMajor控件WinForms 绑定依赖DataPropertyName与属性名严格匹配
校验逻辑Utils/ValidationHelper.cs在ValidateStudent()方法中添加!string.IsNullOrWhiteSpace(student.Major)检查防止专业为空导致数据库插入失败(若设了NOT NULL约束)

验证方法:修改后重新编译,进入「学生管理」页,点击「新增」,填写专业名后保存,刷新列表,确认新列显示且数据入库。切记:改完数据库后必须重新执行InitDB.sql(或手动ALTER TABLE Student ADD Major NVARCHAR(30)),否则SELECT * FROM Student仍不返回该字段。

5.2 成绩导出为 PDF:用 iTextSharp 替代 Excel(轻量级方案)

项目当前只支持 Excel 导出,但答辩时常被问「能导 PDF 吗?」。iTextSharp 是 .NET 经典 PDF 库,体积小(单 dll < 1MB),无需 Office 依赖。

  1. 安装 NuGet 包:搜索iTextSharp.LGPLv2.Core(开源免费版);
  2. 在ScoreDAL.cs中添加方法:
    public void ExportToPdf(List<Score> scores, string outputPath) { using (var fs = new FileStream(outputPath, FileMode.Create)) using (var document = new Document(PageSize.A4, 25, 25, 30, 30)) { PdfWriter.GetInstance(document, fs); document.Open(); var table = new PdfPTable(4) { WidthPercentage = 100 }; table.AddCell("学号"); table.AddCell("姓名"); table.AddCell("课程"); table.AddCell("成绩"); foreach (var s in scores) { var student = StudentDAL.GetStudentByID(s.StudentID); var course = CourseDAL.GetCourseByID(s.CourseID); table.AddCell(s.StudentID); table.AddCell(student?.Name ?? ""); table.AddCell(course?.CourseName ?? ""); table.AddCell(s.Score.ToString("0.0")); } document.Add(table); } }
  3. 在FrmScoreInput.cs中调用:ExportToPdf(scoreDAL.GetScoresByStudentID(id), "scores.pdf")。

注意:iTextSharp 5.x 版本有 LGPL 限制,若用于商业项目需购买授权;本项目用iTextSharp.LGPLv2.Core(MIT 协议)规避风险。导出 PDF 比 Excel 多 3 行代码,但少了 NPOI 的复杂样式控制,够用即止。

5.3 权限分级:从单管理员到「教师+学生」双角色

当前系统只有AdminUser表,登录后全是超级权限。增加角色需三处改动:

  1. 扩表:在InitDB.sql中为AdminUser表添加Role VARCHAR(10)列(值为'Admin'/'Teacher'/'Student');
  2. 改登录逻辑:LoginDAL.ValidateUser()返回Tuple<bool, string>,第二个值为角色;FormMain.cs根据角色动态禁用 Tab 页:
    if (role == "Student") { tabControl1.TabPages.Remove(tabPageScore); // 学生不能录成绩 tabControl1.TabPages.Remove(tabPageElective); // 学生只能看自己的选课 }
  3. 改数据查询:ScoreDAL.GetScoresByStudentID()在学生角色下调用时,增加AND StudentID = @currentID条件,防止学生查他人成绩。

从那以后我每次接到「加权限」需求,都强制走一遍这三步 checklist:先改 DB Schema,再改 Login 返回值,最后按角色切 UI 和 DAL 查询条件。漏任何一步都会导致权限绕过,答辩时被导师当场抓包。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询