C#酒店客房管理系统课设实战:WinForms+SQL Server数据库设计
简介一套基于C#和SQL Server 2005的酒店客房管理系统课程设计完整资料包含源码、数据库与结课报告适合计算机相关专业学生完成课程设计、毕业设计或进行同类项目二次开发。系统覆盖登录、用户添加、房间管理与房间类型编辑、物品管理、预订查询、破损物品查询、客人记录、员工设置、客户入住、出租方式管理及退房等环节客房与客人独立管理业务链条完整。代码采用窗体应用结构按模块划分目录层级清楚便于分部分阅读理解。压缩包共369个文件总体积仅3.08MB。内容以131个C#源文件为核心另含resx界面资源、xsd数据集及其设计文件、dll依赖库、exe可执行程序以及SQL Server数据库文件mdf/ldf和doc格式设计报告源码、可运行程序、数据库与文档一并打包可直接加载到开发环境查看或继续扩展。目前已有37人学习下载适合用来对照练习C#窗体开发、SQL Server数据操作和酒店信息管理业务设计。1. 这个zip包里到底有什么C#酒店客房管理系统不止是“交差”很多人的课程设计就是从这样一个压缩包开始的名字叫「课程设计-基于C#的酒店客房管理系统源码数据库报告.zip」解压之后是C#工程、数据库文件、Word版报告三件套。别小看这个组合它其实是典型的“能跑、能讲、能改”的课设样板——C#窗体程序负责和用户交互数据库负责把客房状态、预订和入住记录管住报告负责把设计过程讲清楚。你拿到的不是一堆死代码而是一条从需求到实现再到答辩的完整链路。它适合三类人正在做C#课程设计、急需一个可复现项目的大三学生想快速搭一套桌面端管理系统练手的C#入门者以及接手维护这类老系统、需要马上理解代码结构的后来者。2. 选型先于编码C#课设用WinForms还是WPF数据库选SQL Server还是SQLite动工之前把技术栈定下来比写代码更重要。课程设计不同于企业项目评分标准里“能演示、能解释、报告完整”的权重远高于“架构优雅”所以选型的核心逻辑是别为了炫技给自己挖坑要选你三天后还能讲清楚的那套方案。2.1 WinForms还是WPF课设时间有限我一般劝你选WinForms解压这类资源包最常见的工程形态是WinForms窗体项目。原因很直白WinForms的拖拽式设计器能让新手在一个晚上搭出完整界面控件属性、事件处理都在面板里看得到不存在“界面突然渲染不出来”的黑匣子问题。WPF虽然界面更现代但牵扯到XAML、数据绑定、样式模板任何一个环节出问题都会消耗大量调试时间这在课程设计的时间表里是致命的。选择WinForms还有一个隐性好处它在报告里好写。你可以在报告里用一两页讲清楚“我用窗体承载业务、用按钮事件触发逻辑、用DataGridView展示查询结果”换成WPF就要解释依赖属性、命令绑定解释成本高了一个数量级。我见过不止一个同学因为选了WPF在答辩时被老师一句“这里为什么用Binding而不是CodeBehind”问得支支吾吾。版本方面常见做法是使用.NET Framework 4.x创建WinForms项目因为学校机房、老师提供的虚拟机里大概率装的就是这个运行时。如果你的老师允许用新版.NET的Windows Forms也可以跑但要注意目标框架和运行库差异宁可保守一点别让版本问题毁掉整个演示。2.2 数据库选型SQL Server适合交差SQLite适合现场演示不翻车酒店客房管理系统的数据库选型有两个主流SQL Server和SQLite。这两个我都用过给你一个实用判断SQL Server是课设的“标准答案”。理由很简单老师验收时最常见的动作是打开你的数据库文件、看一下表结构和数据SQL Server的备份文件天然适配这个场景你还可以在报告中名正言顺地写“采用SQL Server存储业务数据”显得正规。缺点是部署依赖数据库服务如果演示机器上没装SQL Server系统直接起不来。SQLite是我做现场演示时的“后悔药”。它是单文件数据库不需要安装服务你的程序用一行连接字符串就能打开一个.db文件演示时绝不会出现“服务没启动”这种尴尬。缺点是并发写入能力弱且文件型数据库看起来不如SQL Server“专业”报告里要多解释几句。我的建议很直接如果条件允许主用SQL Server同时把数据访问层封装好让程序能切换到SQLite做备用你不用实现完整的数据同步软件那种复杂逻辑只要在连接字符串层面做切换就够了。下面这张表把我的选型标准列清楚对比项SQL ServerSQLite部署复杂度需安装/启动服务零部署随文件走老师验收友好度高可附加mdf文件查看中需要解释文件型数据库并发能力适合多用户单写多读课设够用报告写作难度低标准企业方案中需要说明取舍理由2.3 项目骨架三层的价值在于报告好写、代码好改资源包里如果只有一个Form1.cs塞满全部代码那是典型的“能跑但不好改”。拿到源码后我建议第一时间把它整理成三层结构界面层Forms只放控件和事件调用业务层BLL放房态流转、价格计算这类规则数据层DAL放所有SQL语句和参数化命令。这么做有两个实际好处。第一老师问你“订单取消后房间状态怎么改”时你能直接指出业务层里的那个方法而不是在一千行事件代码里翻找。第二后面要加功能比如加一个会员折扣只需要动业务层和数据库字段不必重画界面。这是我在维护老系统时最深的体会——课设看起来是一锤子买卖但你答辩前一周改需求时会感谢当时的自己。目录结构通常是这样的HotelManagement/ ├── HotelManagement.sln ├── HotelManagement.UI/ // 界面层放Form窗体 │ ├── Forms/ │ │ ├── LoginForm.cs │ │ ├── MainForm.cs │ │ └── RoomManageForm.cs ├── HotelManagement.BLL/ // 业务层放业务规则 │ ├── RoomService.cs │ └── ReservationService.cs ├── HotelManagement.DAL/ // 数据层放数据库访问 │ ├── DbHelper.cs │ └── RoomDao.cs └── HotelManagement.Model/ // 实体层放数据模型写代码时注意C#控件命名规范这也是一个容易被忽视的加分点。按钮用btn前缀、文本框用txt前缀、下拉框用cbo前缀、DataGridView用dgv前缀比如btnLogin、txtUserName、cboRoomType、dgvRoomList。别小看这个习惯答辩时老师扫一眼你的源码命名规整与否直接决定他对你代码能力的判断。我见过太多人写button1、textBox2这种默认名功能明明实现了印象分却先扣一截。为了让你能直接抄作业我给一个最简的DbHelper类的写法它负责打开连接、执行SQL、返回结果数据层其他类都依赖它using System; using System.Data; using System.Data.SqlClient; namespace HotelManagement.DAL { public class DbHelper { // 连接字符串统一放在这里也可以迁移到App.config里 private static readonly string ConnStr Data Source.;Initial CatalogHotelDb;Integrated SecurityTrue;; /// summary /// 执行增删改语句写库时用返回受影响的行数 /// /summary public static int ExecuteNonQuery(string sql, params SqlParameter[] parameters) { using (SqlConnection conn new SqlConnection(ConnStr)) using (SqlCommand cmd new SqlCommand(sql, conn)) { if (parameters ! null) { cmd.Parameters.AddRange(parameters); } conn.Open(); return cmd.ExecuteNonQuery(); } } /// summary /// 执行查询语句读库时用返回DataTable供DataGridView绑定 /// /summary public static DataTable ExecuteQuery(string sql, params SqlParameter[] parameters) { using (SqlConnection conn new SqlConnection(ConnStr)) using (SqlDataAdapter adapter new SqlDataAdapter(sql, conn)) { if (parameters ! null) { adapter.SelectCommand.Parameters.AddRange(parameters); } DataTable dt new DataTable(); adapter.Fill(dt); return dt; } } } }这段代码有两个关键做法。第一所有对象都包在using块里确保数据库连接用完即关避免连接泄漏——这是课设里最常见的隐藏扣分点。第二参数以SqlParameter数组传入目的不是炫技是防SQL注入也是答辩时老师最愿意看到的“底线意识”。你在报告里把这两点写出来数据库部分的专业度立刻就立住了。课程设计不是C#高级编程考试不需要把ORM、依赖注入、微服务这些词搬进报告凑篇幅。把WinForms、ADO.NET、三层结构这三板斧讲透比堆砌概念更靠近高分。3. 数据库设计与增删改查五张表定生死SQL脚本要能一遍跑通不管你拿到的是.sql脚本、.mdf附加文件还是.bak备份第一步都是把库建出来、把数据灌进去。很多人在这一步卡住原因是SQL Server版本不一致、文件附加权限不够、脚本不兼容。所以我的方法很简单拿到源码后不要急着用现成文件先手写一份建库脚本把表和测试数据完整地重建一遍。3.1 五张核心表房型表、客房表、顾客表、预订表、入住表酒店客房管理系统的业务本质是“房间状态随时间流转”数据库里最少需要五张表才能把这个过程讲清楚。下面我用字段表格给出一套标准设计你可以直接抄进报告当ER图说明。房型表RoomType价格挂在房型上而不是房间上这样“商务大床房”有十间房时不用重复存十次价格。字段名类型说明IdINT 主键自增房型编号TypeNameNVARCHAR(50)房型名称如标准间PriceDECIMAL(10,2)门市价按天计费BedCountINT床位数影响入住人数校验RemarkNVARCHAR(200)备注比如“含早”客房表Room房间是物理实体状态是它最关键的字段。状态设计成INT比VARCHAR好程序里用枚举对应这样不会出现“空闲”和“空房”两种写法并存的数据脏问题。字段名类型说明IdINT 主键自增房间编号RoomNoNVARCHAR(20)门牌号如301TypeIdINT 外键关联RoomType.IdFloorINT所在楼层StatusINT0空闲 1已预订 2已入住 3脏房顾客表Customer顾客信息是预订和入住都要用的单独建表避免在两张单里重复存姓名和电话。身份证号建议加唯一索引但界面上展示时要打码处理。字段名类型说明IdINT 主键自增顾客编号NameNVARCHAR(50)姓名PhoneNVARCHAR(20)联系电话IDCardNVARCHAR(18)身份证号CreateTimeDATETIME建档时间预订表Reservation预订表达的是“未来要住”的意向它和入住表分开记录才能回答“明天有哪些预订到店”这类运营问题。Status字段区分待入住、已入住、已取消。字段名类型说明IdINT 主键自增预订编号RoomIdINT 外键预定的房间CustomerIdINT 外键顾客StartDateDATE预计入住日期EndDateDATE预计离店日期StatusINT0待入住 1已入住 2已取消入住表CheckIn入住记录是系统的账单核心结账时统计金额就靠它。TotalPrice这里是冗余字段预订时算好价格存下来因为房价后续可能调价以入住当时的价格为准。字段名类型说明IdINT 主键自增入住编号RoomIdINT 外键入住的房间CustomerIdINT 外键顾客CheckInTimeDATETIME入住时间CheckOutTimeDATETIME离店时间TotalPriceDECIMAL(10,2)应收金额离店时计算字段设计上有两个容易忽略的点。一个是金额一律用DECIMAL(10,2)而不用FLOAT浮点误差在钱上不可接受这个我在刚入门时吃过亏另一个是日期用DATE还是DATETIME要想清楚预订只关心天入住结账关心到分钟混用会导致日期格式化出来的值对不上。3.2 建库脚本一份能直接灌进SQL Server的SQL下面这份脚本是我常用的标准建库方式包含建库、建表、插测试数据三个步骤全部执行成功后才能证明环境没问题。-- 建库实际使用时改成你自己的数据库名 CREATE DATABASE HotelDb; GO USE HotelDb; GO -- 房型表 CREATE TABLE RoomType ( Id INT IDENTITY(1,1) PRIMARY KEY, TypeName NVARCHAR(50) NOT NULL, Price DECIMAL(10,2) NOT NULL, BedCount INT NOT NULL DEFAULT 1, Remark NVARCHAR(200) ); -- 客房表TypeId外键指向房型表 CREATE TABLE Room ( Id INT IDENTITY(1,1) PRIMARY KEY, RoomNo NVARCHAR(20) NOT NULL UNIQUE, TypeId INT NOT NULL FOREIGN KEY REFERENCES RoomType(Id), Floor INT NOT NULL, Status INT NOT NULL DEFAULT 0 ); -- 顾客表 CREATE TABLE Customer ( Id INT IDENTITY(1,1) PRIMARY KEY, Name NVARCHAR(50) NOT NULL, Phone NVARCHAR(20) NOT NULL, IDCard NVARCHAR(18) NOT NULL UNIQUE, CreateTime DATETIME NOT NULL DEFAULT GETDATE() ); -- 预订表 CREATE TABLE Reservation ( Id INT IDENTITY(1,1) PRIMARY KEY, RoomId INT NOT NULL FOREIGN KEY REFERENCES Room(Id), CustomerId INT NOT NULL FOREIGN KEY REFERENCES Customer(Id), StartDate DATE NOT NULL, EndDate DATE NOT NULL, Status INT NOT NULL DEFAULT 0 ); -- 入住记录表 CREATE TABLE CheckIn ( Id INT IDENTITY(1,1) PRIMARY KEY, RoomId INT NOT NULL FOREIGN KEY REFERENCES Room(Id), CustomerId INT NOT NULL FOREIGN KEY REFERENCES Customer(Id), CheckInTime DATETIME NOT NULL DEFAULT GETDATE(), CheckOutTime DATETIME, TotalPrice DECIMAL(10,2) ); GO -- 测试数据两种房型三间房一位顾客一条预订 INSERT INTO RoomType (TypeName, Price, BedCount) VALUES (N标准间, 238.00, 2); INSERT INTO RoomType (TypeName, Price, BedCount) VALUES (N商务大床房, 368.00, 1); INSERT INTO Room (RoomNo, TypeId, Floor, Status) VALUES (N201, 1, 2, 0); INSERT INTO Room (RoomNo, TypeId, Floor, Status) VALUES (N202, 1, 2, 0); INSERT INTO Room (RoomNo, TypeId, Floor, Status) VALUES (N301, 2, 3, 2); INSERT INTO Customer (Name, Phone, IDCard) VALUES (N张三, N13800138000, N123456200001011234); INSERT INTO Reservation (RoomId, CustomerId, StartDate, EndDate, Status) VALUES (1, 1, CAST(GETDATE() AS DATE), DATEADD(DAY, 2, CAST(GETDATE() AS DATE)), 0); GO这段脚本里值得注意的参数有三个。一个是NVARCHAR字段长度中文姓名、地址、备注都可能超过英文场景的预期宁长勿短第二个是Status字段的默认值设为0这样插入房间时不写状态也默认是空闲减少漏赋值导致的逻辑错误第三个是测试数据里的日期用了GETDATE()和DATEADD动态生成这样不管哪天执行脚本预订日期都是“今天到后天”演示时永远能看到一条有效的待入住记录。执行脚本时如果报错最常见的不是语法错而是重复执行——表已经存在再CREATE就会失败。我习惯在脚本开头加一句IF OBJECT_ID(NRoom, NU) IS NOT NULL DROP TABLE Room;这类判断但要按外键依赖顺序从子表往父表删否则外键约束会挡住。另外很多课设资源包里其实没有完整建表脚本只有一个附加上来的.mdf文件这种情况我会先在SSMS里看表结构再右键数据库选择“编写脚本为CREATE”反推出建库语句你遇到时自然知道去哪找。3.3 数据库增删改查客房管理系统最核心的三条SQL掌握了建库之后程序里跑得最频繁的无非是增删改查CRUD。酒店管理系统里最重要的三条SQL我按出现频率给你排一下。第一条是“查询当前可用的空闲房”它支撑预订界面的房态展示-- 查空闲房关联房型获取价格只取Status0的房间 SELECT r.Id AS RoomId, r.RoomNo, t.TypeName, t.Price, r.Floor FROM Room r INNER JOIN RoomType t ON r.TypeId t.Id WHERE r.Status 0 ORDER BY r.Floor ASC, r.RoomNo ASC;第二条是“新增预订并锁房”预订操作必须同时写Reservation表和改Room表保证房间不会被二次预订-- 开启事务保证两步要么都成功要么都失败 BEGIN TRANSACTION; INSERT INTO Reservation (RoomId, CustomerId, StartDate, EndDate, Status) VALUES (RoomId, CustomerId, StartDate, EndDate, 0); -- 把对应房间置为已预订 UPDATE Room SET Status 1 WHERE Id RoomId AND Status 0; IF ROWCOUNT 0 BEGIN ROLLBACK TRANSACTION; -- 这里应该抛异常给C#端提示“房间已被占用” END ELSE BEGIN COMMIT TRANSACTION; END第三条是“离店结账”先算金额再改房态报告里写清楚这笔账的计算口径答辩基本不会被追问-- 按入住到离店的跨夜数算房费房费从房型表带出 DECLARE Nights INT DATEDIFF(DAY, CheckInTime, CheckOutTime); DECLARE Price DECIMAL(10,2) (SELECT t.Price FROM Room r INNER JOIN RoomType t ON r.TypeId t.Id WHERE r.Id RoomId); -- 更新入住记录并释放房间 UPDATE CheckIn SET CheckOutTime CheckOutTime, TotalPrice Nights * Price WHERE Id CheckInId; UPDATE Room SET Status 0 WHERE Id RoomId;这三条SQL覆盖了“查询”“插入更新”“更新计算”三种基本形态写程序时把它翻译成参数化命令即可。注意第二条和第三条都用到了事务或状态条件的写法这是数据库并发下防止超卖、重复入住的关键。你在报告里写“通过事务与状态条件更新保证房态一致性”老师一眼就知道你不是只会写增删改查。4. 在Visual Studio里跑通源码登录、房态流转和报表核心代码数据库就位后C#工程那边是最容易出状况的地方。课设源码最常见的问题是别人机器上跑得好好的你打开就编译报错或者能编译但运行时连接不上数据库。这一章我把登录、客房状态流转、报表展示三个核心模块的写法讲透你拿到源码后直接对照改。4.1 用ADO.NET还是EF Core课设场景我用ADO.NET.NET生态里访问数据库有两条路ADO.NET手写SQL或者EF Core等ORM框架。课程设计这个场景我明确建议用ADO.NET。这听起来像是在绕远路但课设的项目规模就五张表ORM引入的实体映射、迁移、延迟加载每一个概念都是报告的负担而且一旦EF版本和Visual Studio环境不兼容容易陷入配置泥潭。ADO.NET的核心就三个类SqlConnection负责连接、SqlCommand负责执行、SqlDataAdapter负责把结果填充到DataTable。前面写的DbHelper已经把三者封装好了后面的业务代码只需要传SQL和参数进去代码量一点也不比ORM多。我推荐ADO.NET还有答辩层面的考虑老师大概率会追着问“你的数据库是怎么访问的”你回答“我封装了一个DbHelper通过参数化SqlCommand执行SQL”和回答“我用了EF CoreDbContext自动映射”前者对话会越来越具体可控后者则会引向映射原理和性能优化等更难招架的话题。课程设计不是C#高级编程考试把有限的时间花在能稳定拿分的地方。4.2 登录模块哈希存储和参数化查询缺一不可登录几乎是所有管理系统的门户也是老师必看的代码。常见做法是数据库里存用户名和密码登录时校验。这里有个很容易被忽略的安全细节密码不能明文存。你用SHA256计算一个哈希值存到数据库里登录时把输入密码再算一次哈希比对两个哈希值是否一致。先看数据库侧用户表一般是附带的你包里如果没有可以自己加一张CREATE TABLE SysUser ( Id INT IDENTITY(1,1) PRIMARY KEY, UserName NVARCHAR(50) NOT NULL UNIQUE, PasswordHash NVARCHAR(64) NOT NULL, DisplayName NVARCHAR(50) );然后看C#端登录校验代码using System; using System.Data; using System.Security.Cryptography; using System.Text; using System.Windows.Forms; using HotelManagement.DAL; namespace HotelManagement.UI.Forms { public partial class LoginForm : Form { public LoginForm() { InitializeComponent(); } // 登录按钮点击事件btnLogin是登录按钮txtUserName和txtPassword是输入框 private void btnLogin_Click(object sender, EventArgs e) { string userName txtUserName.Text.Trim(); string password txtPassword.Text; // 输入为空直接提示避免把空值送到数据库 if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password)) { MessageBox.Show(请输入用户名和密码); return; } // 对密码做SHA256哈希数据库中存的是这个哈希值而不是明文 string passwordHash ComputeSha256(password); // 参数化查询用户名和密码哈希都通过SqlParameter传递 string sql SELECT DisplayName FROM SysUser WHERE UserName UserName AND PasswordHash PasswordHash; SqlParameter[] parameters new SqlParameter[] { new SqlParameter(UserName, userName), new SqlParameter(PasswordHash, passwordHash) }; DataTable dt DbHelper.ExecuteQuery(sql, parameters); if (dt.Rows.Count 1) { // 登录成功把显示名带到主窗体 MessageBox.Show(欢迎 dt.Rows[0][DisplayName]); MainForm mainForm new MainForm(); mainForm.Show(); this.Hide(); } else { MessageBox.Show(用户名或密码错误); } } /// summary /// 计算SHA256哈希返回十六进制字符串 /// /summary private string ComputeSha256(string input) { using (SHA256 sha256 SHA256.Create()) { byte[] bytes Encoding.UTF8.GetBytes(input); byte[] hash sha256.ComputeHash(bytes); StringBuilder sb new StringBuilder(); foreach (byte b in hash) { // 每个字节转两位十六进制拼成64位字符串 sb.Append(b.ToString(x2)); } return sb.ToString(); } } } }这段代码里有三个点要在报告里写清楚。第一密码哈希化就算数据库文件被人拷走拿到的也不是明文密码——这是安全底线说了加分。第二.Trim()和null判断课程设计里经常出现文本框留空直接点登录然后程序崩掉的情况这个防御在前端先做掉。第三参数化SQL里用了UserName和PasswordHash占位符没有任何字符串拼接这是对SQL注入的标准防御。老师看到这三处对你的印象会明显高于只会拖控件的同学。4.3 客房状态流转用枚举加状态机把规则讲明白客房管理系统的灵魂不是界面是房态流转的规则。空闲房被预订变“已预订”、客人到店变“已入住”、退房后变“脏房”、保洁完变“空闲”这四态在程序里如果用魔法数字满天飞代码会非常难维护而且报告根本没法写。我建议用枚举定义状态再在业务层提供统一的方法完成状态迁移。namespace HotelManagement.Model { /// summary /// 房态枚举和数据库里Room.Status字段的数字一一对应 /// /summary public enum RoomStatus { Available 0, // 空闲 Reserved 1, // 已预订 Occupied 2, // 已入住 Dirty 3 // 脏房待保洁 } }配合房态枚举业务层写一个方法专门处理“入住”操作它同时要做三件事改房态、写入住记录、把预订单状态置为已入住。这三个动作必须在一个事务里完成否则会出现房间已入住但预订单还是待入住的脏数据。代码示意如下using System; using System.Data; using HotelManagement.DAL; using HotelManagement.Model; namespace HotelManagement.BLL { public class CheckInService { /// summary /// 客人到店入住把预订转入住房间状态改为已入住 /// /summary public bool CheckIn(int reservationId, int roomId, int customerId) { // 三步操作封装在一个事务里任何一步失败都回滚 string sql BEGIN TRANSACTION; -- 更新预订单状态为已入住 UPDATE Reservation SET Status 1 WHERE Id ReservationId AND Status 0; -- 插入入住记录 INSERT INTO CheckIn (RoomId, CustomerId, CheckInTime) VALUES (RoomId, CustomerId, GETDATE()); -- 更新房间状态为已入住此处限定原状态为1防止重复入住 UPDATE Room SET Status 2 WHERE Id RoomId AND Status 1; IF ROWCOUNT 0 BEGIN ROLLBACK TRANSACTION; -- 返回影响行数0C#端提示错误 END ELSE COMMIT TRANSACTION; ; SqlParameter[] parameters new SqlParameter[] { new SqlParameter(ReservationId, reservationId), new SqlParameter(RoomId, roomId), new SqlParameter(CustomerId, customerId) }; return DbHelper.ExecuteNonQuery(sql, parameters) 0; } } }这个方法的精妙之处在UPDATE Room那句的“AND Status 1”条件。它利用数据库的原子性让“把已预订房改成已入住”这个操作在并发情况下只允许一个事务成功从根本上防止两个人同时入住同一间房。你把这段逻辑写到报告里并把加条件更新的原因解释给老师数据库一致性这关就稳过了。4.4 DataGridView报表一句SQL加三行代码搞定“今日房态表”课程设计里的报表通常不需要专业报表控件用DataGridView展示一张查询结果就足够。比如“今日房态总览”就是一条SQL加三行绑定代码的事// 汇总今日房态每种状态有多少间房 string sql SELECT CASE Status WHEN 0 THEN N空闲 WHEN 1 THEN N已预订 WHEN 2 THEN N已入住 WHEN 3 THEN N脏房 END AS 房态, COUNT(*) AS 房间数 FROM Room GROUP BY Status ORDER BY Status; ; DataTable dt DbHelper.ExecuteQuery(sql); dgvRoomStatus.DataSource dt;这段代码绑定的DataTable会直接显示成表格列名就是SQL里的别名“房态”“房间数”。这里注意一个C#入门者常犯的错DataGridView绑定DataTable后要养成设置AutoGenerateColumns和只读属性的习惯否则用户可能直接在表格里编辑数据。正确的做法是在窗体Load事件里加上dgvRoomStatus.AutoGenerateColumns true;和dgvRoomStatus.ReadOnly true;前者自动生成列后者禁止编辑。课设现场演示时老师手一抖在表格里改了内容你的数据库就变了这会带来许多不必要的麻烦。5. 避坑写课设系统最容易翻车的5个地方我把这些年带人做课设攒下来的血泪经验整理成五条每一条都按“现象→原因→解决”给你说清楚。这些问题不会同时出现但你只要踩中一个答辩那天的体验就会非常糟糕。5.1 连接字符串写死本机地址换电脑就跑不起来现象源码在自己电脑上运行正常拷贝到演示机器上打开就报“在建立到服务器的连接时出错”。原因连接字符串里写的是Data Source.;或localhost换电脑后那台机器上要么没有SQL Server要么实例名不一致。解决把连接字符串放到App.config的connectionStrings节点里演示前检查目标机器。教你一招在演示机器上先打开SQL Server Management Studio看看实例名如果它显示的是“.\SQLEXPRESS”你的连接字符串就要写成Data Source.\SQLEXPRESS。这是源码包里最常见的坑没有之一。更稳的做法是给连接字符串加一个Connection Timeout5参数数据库连不上时程序能快速报错而不是卡住几秒才弹异常现场体验会好很多。App.config里的写法参考configuration connectionStrings add nameHotelDb connectionStringData Source.\SQLEXPRESS;Initial CatalogHotelDb;Integrated SecurityTrue;Connection Timeout5 providerNameSystem.Data.SqlClient / /connectionStrings /configurationC#端读取时用ConfigurationManager.ConnectionStrings[HotelDb].ConnectionString记得在工程里引用System.Configuration程序集很多源码包在这里漏了引用导致编译失败遇到这个报错不要慌加上引用就好。5.2 日期查询“查不到数据”现象按照入住日期筛选订单界面查询结果永远是空的但数据库里明明有数据。原因数据库里存的是DATETIME类型界面传入的日期是“2024-05-20”SQL里写WHERE CheckInTime 2024-05-20时数据库拿它和“2024-05-20 14:32:00”比较自然不相等。解决改成范围查询WHERE CheckInTime StartDate AND CheckInTime DATEADD(DAY, 1, StartDate)这样就把当天所有时间点都覆盖了。这个坑在日期相关的课程设计里出现频率极高我每次看到同学在眼前翻车都替他着急因为你只要提前跑一遍带时间的日期筛选就能发现。C#端把DateTimePicker控件的Value直接作为参数传入即可注意传的是整个DateTime对象不是格式化后的字符串让数据库去处理比较逻辑。5.3 并发场景同一间房被重复预订现象演示时开两个窗口同时预订301房两边都提示成功。原因程序只有“先查状态再插入”的顺序逻辑没有事务保护两个请求都查到“空闲”然后都执行插入。解决把判断和插入放进同一个事务里并在UPDATE Room时加状态条件就像前文“CheckIn”代码写的那样。如果你的源码里没有这段拿到后一定要补上这是整个系统里最能体现设计水平的地方。一个容易被忽略的细节是事务里必须先执行UPDATE锁行再执行INSERT顺序反了会在高并发下出现插入成功但更新失败的情况。你可以在报告里画一张简单的状态回滚说明图不用太复杂把两条并发请求和一条回滚路径画出来就够了。5.4 报告截图和实际系统对不上现象报告里的系统界面截图和答辩现场演示的界面长得不一样老师一眼就能看出来。原因报告是早期写的系统后来改过截图没同步更新。解决拿到源码后先定稿不需要大改的功能统一界面风格全部改完再截一次图。报告中涉及“系统测试结果”的截图必须来自最终运行的版本这是答辩时被老师指出来非常尴尬的情况。我的经验是把截图放在报告最后的测试章节答辩前一天全面跑一遍流程再截不要用开发中期随手截的图。另外截图里不要出现数据库里测试用的脏数据比如“测试1”“111”这种顾客名插入测试数据时就用像“张三”这样正常的名字避免老师觉得你的数据管理意识薄弱。5.5 报错“无法附加数据库”导致整个系统白搭现象双击.mdf文件附加到SQL Server时报权限或版本错误老师那边也附加不上。原因.mdf文件是从另一个SQL Server版本生成版本不兼容或被放在了需要管理员权限的目录下。解决不要依赖附加文件在报告里写清楚“本系统使用脚本建库”并随项目附一份.sql建库脚本。如果有.mdf附加不了的场景我会用第3章那份CREATEINSERT脚本重新建库把测试数据灌进去——这方法虽然老土但永远不会被版本问题卡住。数据库脚本是给你自己的后悔药这个习惯比源码本身值钱。我见过太多人答辩当天在老师机器上折腾数据库附加浪费了最宝贵的十分钟。你在自己的U盘里同时放上.mdf文件、.bak备份和.sql脚本三种形态哪个能用用哪个这才叫有备无患。最后送你一张自查表答辩前一天对着过一遍坑位典型症状最容易发生的时机一句话解法连接字符串换机器连不上数据库现场演示前一天配置外置提前检查实例名日期查询查不到当天数据按日期筛选测试时转成范围查询并发订房同一间房被重复预订多窗口演示事务加状态条件更新截图不一致报告和演示界面不同答辩提问环节测试截图放在最后统一补附加数据库mdf附加报版本/权限错老师拷贝数据库时提供建库脚本代替6. 答辩前还能做的三个小升级把“能用”变成“作品”如果你的核心功能已经能完整跑通答辩时间还有两三天我建议你按性价比做三个小升级。这三个动作工程量都不大但能让老师明显感觉你这个课设“有个作品的样子”。第一个建议把日期选择器换成DateTimePicker。很多源码里预订日期用的是TextBox手输格式一错系统就崩也会被老师质疑健壮性。WinForms自带DateTimePicker控件拖到窗体上设好Format属性用代码限制MaxDate和MinDate范围程序会强制用户选择合法日期无形中消灭了一整类输入异常。查询时用DateTimePicker.Value传给参数配合之前说的范围查询日期功能怎么点都不会翻车。第二个建议给所有DataGridView加上双击下钻。比如房态汇总表在dgvRoomStatus上用户双击“已入住”这一行弹出一个子窗体列出所有已入住的房间及顾客。实现思路很简单利用DataGridView的CellDoubleClick事件读取当前单元格所在行的关键字段再执行带参查询。这个小交互演示起来非常抓眼球老师会认为你在“业务视角”上花了心思。第三个建议加入操作日志表。在数据库里加一张OperationLog表字段是Id、Operator、Action、TargetRoom、OperateTime然后在每个关键按钮事件里插入一条日志记录。表面上只是多写点代码实际上是给整个系统补上了审计能力。答辩时老师问“如果有人误操作退房了怎么办”你答“操作日志里有记录可以追溯”这要比反复强调测试了多少用例有效得多。我自己的习惯是课设系统做完后一定会在报告“系统测试”一节里放一张操作日志的截图证明系统记录每一次关键操作。这比贴十张功能截图更能说明系统是“可用产品”而不是“演示程序”。你如果时间只够做一个升级就做这个日志功能它的投入产出比最高。这三个升级做完你的课程设计就从“照着资源包跑通”变成了“有独立设计痕迹的完整作品”。资源包只是起点老师想看到的是你在这套代码上留下自己的思路——哪怕只是改了一个状态枚举、加了一张日志表、换了一种房态展示方式。希望帮到你。本文还有配套的精品资源点击获取