C#酒店管理系统实战:ADO.NET+事务+房态同步

发布时间:2026/9/15 5:35:43
C#酒店管理系统实战:ADO.NET+事务+房态同步 简介这是一套面向C#初学者与毕业设计学生的酒店管理信息系统实战源码聚焦Windows桌面端开发与SQL Server数据库应用解决中小型酒店日常运营中的客房调度、预订登记、入住退房及账务统计等核心业务需求。资源共91个文件包含31个C#业务逻辑文件如ruzhu.cs、kefangguanli.cs、2个SQL Server数据库文件.mdf与.ldf含2008/2014双版本、1个Visual Studio解决方案.sln及配套配置文件app.config、资源文件.resx和编译产物.dll/.exe整体压缩包仅5.37MB轻量易部署。已有76人学习下载适合用于课程设计、毕设参考或.NET全栈入门实践。读者可直接加载解决方案快速理解分层架构设计——从登录界面、主窗体到各功能模块员工管理、通讯录、黑名单、押金查询等的事件驱动实现掌握数据库连接配置、DataSet数据集绑定、WinForm控件协同及多语言资源本地化等关键技能。1. 这不是“又一个课程设计”而是一套可落地的酒店业务闭环系统你下载到的基于C#的酒店管理系统源码数据库.zip表面看是高校《数据库课程设计》常见交付物但真正能跑通、能改、能上线的并不多。很多所谓“完整系统”卡在登录后白屏、房态不刷新、结账金额错乱——根本原因不是代码写得差而是没理清酒店业务的真实约束房态必须实时互斥、押金与预付需分账处理、退房时要自动触发清洁工单、历史订单不能物理删除。这套 C# 实现恰恰绕开了教科书式 CRUD 堆砌用DataSetSqlDataAdapter做本地缓存同步用Timer控制前台界面房态轮询间隔用TransactionScope包裹入住/退房全流程。它适合两类人刚学完 ADO.NET 想验证真实业务逻辑的开发者以及需要快速搭建内部测试环境的中小酒店 IT 运维人员。注意它不依赖 Entity Framework所有 SQL 都显式编写方便你直接对照《数据库增删改查》规范逐条审计。2. 从 ZIP 解压到数据库就绪三步完成环境初始化2.1 解压后必须校验的 4 个关键文件结构下载的.zip文件解压后应包含以下目录层级缺失任一即为残缺包HotelSystem/ ├── HotelSystem.sln ← Visual Studio 解决方案文件VS 2019 兼容 ├── HotelSystem/ ← 主项目目录 │ ├── Program.cs ← 入口点含 Main 方法 │ ├── frmLogin.cs ← 登录窗体含用户名/密码验证逻辑 │ └── ... ← 其他窗体与业务类 ├── Database/ ← 数据库脚本存放目录 │ ├── HotelDB.sql ← 完整建库建表初始数据含 5 类房型、3 个员工账号 │ └── Backup/ ← 备份脚本含每日凌晨自动备份逻辑 └── Resources/ ← 图标与报表模板.rdlc 文件提示若解压后无Database/HotelDB.sql说明该包未提供数据库初始化能力后续所有操作将失败。不要尝试用 Navicat 或 SSMS 直接导入空库——此系统依赖IDENTITY列和DEFAULT GETDATE()约束必须通过.sql脚本执行。2.2 在 SQL Server 中执行 HotelDB.sql 的实操命令使用 SQL Server Management Studio (SSMS) 连接本地实例如localhost\SQLEXPRESS新建查询窗口粘贴并执行以下命令-- 第一步创建数据库若已存在则跳过 IF NOT EXISTS (SELECT name FROM sys.databases WHERE name HotelDB) CREATE DATABASE HotelDB; GO -- 第二步切换到目标库 USE HotelDB; GO -- 第三步执行建表语句此处仅展示核心表结构实际脚本含 12 张表 CREATE TABLE Rooms ( RoomID INT IDENTITY(1,1) PRIMARY KEY, RoomNumber NVARCHAR(10) NOT NULL UNIQUE, RoomType NVARCHAR(20) NOT NULL, -- 标准间,豪华套房等 Price DECIMAL(10,2) NOT NULL, Status NVARCHAR(10) DEFAULT 空闲 CHECK (Status IN (空闲,已预订,入住中,维修中)) ); GO -- 第四步插入初始房型数据共 20 间房覆盖 5 种类型 INSERT INTO Rooms (RoomNumber, RoomType, Price) VALUES (101,标准间,280.00),(102,标准间,280.00),(201,商务大床,380.00), (301,豪华套房,680.00),(401,行政套房,980.00); GO注意HotelDB.sql中INSERT语句使用GO分隔批处理若在 SSMS 中执行报错“无法解析 GO”请确认查询窗口右下角状态栏显示“SQLCMD 模式”已关闭默认关闭即可。执行成功后在对象资源管理器中展开HotelDB → 表应看到Rooms、Guests、Reservations、CheckInRecords等 12 张表且Rooms表中Status列默认值为空闲。2.3 修改连接字符串让 C# 程序找到你的数据库打开HotelSystem/Properties/Settings.settings文件或直接编辑HotelSystem/Properties/Settings.Designer.cs定位到ConnectionString设置项。原始值通常为Data Source.;Initial CatalogHotelDB;Integrated SecurityTrue;若你的 SQL Server 实例名非默认如localhost\\MSSQLSERVER2022需修改为Data Sourcelocalhost\\MSSQLSERVER2022;Initial CatalogHotelDB;Integrated SecurityTrue;关键参数说明Data SourceSQL Server 实例地址本地命名实例格式为服务器名\\实例名Initial Catalog必须与.sql脚本中CREATE DATABASE名称完全一致区分大小写Integrated SecurityTrue启用 Windows 身份验证无需用户名密码——这是该系统默认安全策略避免明文存储 DB 凭据。3. 登录与核心业务流用 C# 窗体验证数据库交互真实性3.1 登录窗体frmLogin.cs中的数据库验证逻辑拆解登录按钮点击事件btnLogin_Click内部调用ValidateUser方法其核心代码如下private bool ValidateUser(string username, string password) { string query SELECT COUNT(*) FROM Users WHERE Usernameuser AND Passwordpwd; using (SqlConnection conn new SqlConnection(Properties.Settings.Default.ConnectionString)) { using (SqlCommand cmd new SqlCommand(query, conn)) { cmd.Parameters.AddWithValue(user, username); // 防止 SQL 注入 cmd.Parameters.AddWithValue(pwd, password); conn.Open(); int count (int)cmd.ExecuteScalar(); // 返回 0 或 1 return count 0; } } }逻辑说明使用user和pwd参数化查询而非字符串拼接规避admin OR 11类注入攻击ExecuteScalar()仅获取第一行第一列值COUNT(*)结果比ExecuteReader()更轻量密码未加密存储明文这是该系统为简化教学做的妥协生产环境必须替换为BCrypt.Net-Next加密。3.2 房态管理窗体frmRoomStatus.cs的实时同步机制该窗体加载时执行LoadRoomData()方法关键代码段private void LoadRoomData() { string sql SELECT r.RoomNumber, r.RoomType, r.Price, r.Status, g.GuestName, c.CheckInTime FROM Rooms r LEFT JOIN CheckInRecords c ON r.RoomID c.RoomID AND c.CheckOutTime IS NULL LEFT JOIN Guests g ON c.GuestID g.GuestID; SqlDataAdapter adapter new SqlDataAdapter(sql, conn); DataTable dt new DataTable(); adapter.Fill(dt); // 一次性拉取全部房态 dgvRooms.DataSource dt; // 启动定时器每 30 秒刷新一次避免频繁查询 timerRefresh.Interval 30000; timerRefresh.Start(); }参数说明LEFT JOIN确保即使房间空闲无CheckInRecords记录仍显示在表格中c.CheckOutTime IS NULL条件过滤出“当前入住中”的记录这是判断Status入住中的依据timerRefresh的Tick事件中会再次调用adapter.Fill(dt)实现伪实时更新——比 SignalR 轻量适合局域网部署。3.3 入住登记frmCheckIn.cs中的事务控制要点提交入住时btnConfirm_Click方法包裹三层操作using (var scope new TransactionScope()) { try { // 步骤1插入 Guests 表获取新 GuestID int guestId InsertGuest(guestInfo); // 步骤2更新 Rooms 表状态为 入住中 UpdateRoomStatus(roomId, 入住中); // 步骤3插入 CheckInRecords 表关联 GuestID 和 RoomID InsertCheckInRecord(guestId, roomId, DateTime.Now); scope.Complete(); // 仅当三步全成功才提交 MessageBox.Show(入住成功); } catch (Exception ex) { MessageBox.Show($入住失败{ex.Message}); // scope.Dispose() 自动回滚所有操作 } }关键约束若InsertGuest成功但UpdateRoomStatus失败如房间已被他人抢占整个事务回滚Guests表不会残留脏数据TransactionScope默认隔离级别为Serializable防止并发入住时出现超卖——这是酒店系统区别于普通 CRUD 的核心。4. 常见运行故障与精准排错路径4.1 “登录失败无法打开登录数据库” 错误的三层定位法该错误 90% 源于连接字符串配置按顺序检查检查层级操作命令/位置期望结果实例层在 CMD 执行sqlservr.exe -m查看 SQL Server 是否运行返回进程 PID 或提示“服务未启动”库层SSMS 中执行SELECT name FROM sys.databases WHERE nameHotelDB返回单行HotelDB权限层在 SSMS 中右键HotelDB → 属性 → 权限确认当前 Windows 用户有db_owner角色“显式”列显示勾选注意若使用 SQL Server Express 版本需确认SQL Server (SQLEXPRESS)服务已启动services.msc中查找且防火墙允许1433端口入站。4.2 DataGridView 显示空白但无报错的 3 个隐藏原因当dgvRooms.DataSource dt后表格为空排除网络和数据库连接后重点检查DataTable 列名与 DataGridView 自动生成列名不匹配在LoadRoomData()中添加调试输出Console.WriteLine($DataTable 列数{dt.Columns.Count}); foreach (DataColumn col in dt.Columns) Console.WriteLine($列名{col.ColumnName}类型{col.DataType.Name});若输出RoomNumber但dgvRooms.Columns[0].HeaderText显示roomnumber小写需在绑定前设置dgvRooms.AutoGenerateColumns false; dgvRooms.Columns[RoomNumber].HeaderText 房间号;SQL 查询返回空结果集但未抛异常在adapter.Fill(dt)后添加断言if (dt.Rows.Count 0) throw new Exception(房态查询返回空集请检查 Rooms 表是否有数据);Windows Forms 线程模型冲突若在Timer.Tick中异步刷新DataTable必须用Invokethis.Invoke((MethodInvoker)delegate { dgvRooms.DataSource dt; });否则 UI 线程无法安全更新控件。4.3 修改房价后立即生效的验证方法系统未提供后台管理界面修改房价需直接操作数据库-- 将 101 号房价格改为 320 元 UPDATE Rooms SET Price 320.00 WHERE RoomNumber 101;然后在frmRoomStatus.cs的timerRefresh_Tick事件中添加日志输出private void timerRefresh_Tick(object sender, EventArgs e) { Console.WriteLine($[{DateTime.Now:HH:mm:ss}] 开始刷新房态...); LoadRoomData(); // 此处会重新查询新价格立即显示 }观察控制台输出时间戳与 DataGridView 更新时间差若超过 30 秒说明timerRefresh未正确启动——检查LoadRoomData()末尾是否遗漏timerRefresh.Start()。5. 基于现有源码的 3 个高价值扩展方向5.1 为房态表格增加“快速筛选”功能C# 显示查找一条记录字段数据当前dgvRooms仅支持滚动查看添加顶部搜索框实现按房号/房型模糊查找// 在 frmRoomStatus.cs 中添加 TextBox txtSearch 和 Button btnSearch private void btnSearch_Click(object sender, EventArgs e) { string filter $RoomNumber LIKE %{txtSearch.Text}% OR RoomType LIKE %{txtSearch.Text}%; (dgvRooms.DataSource as DataTable).DefaultView.RowFilter filter; }注意DefaultView.RowFilter支持LIKE语法但txtSearch.Text必须做基础清洗移除单引号否则可能引发异常。更安全的做法是使用BindingSourceBindingSource bs new BindingSource(); bs.DataSource dt; dgvRooms.DataSource bs; bs.Filter $RoomNumber LIKE %{CleanInput(txtSearch.Text)}%;其中CleanInput方法移除和;字符。5.2 导出当日入住明细为 Excel规避 zip 压缩包密码破解工具依赖系统未内置导出但可利用Microsoft.Office.Interop.Excel快速实现private void ExportToExcel() { string sql SELECT r.RoomNumber, g.GuestName, g.IDCard, c.CheckInTime FROM CheckInRecords c INNER JOIN Rooms r ON c.RoomID r.RoomID INNER JOIN Guests g ON c.GuestID g.GuestID WHERE CAST(c.CheckInTime AS DATE) CAST(GETDATE() AS DATE); SqlDataAdapter adapter new SqlDataAdapter(sql, conn); DataTable reportDt new DataTable(); adapter.Fill(reportDt); var excelApp new Microsoft.Office.Interop.Excel.Application(); var workbook excelApp.Workbooks.Add(); var worksheet workbook.ActiveSheet as Microsoft.Office.Interop.Excel.Worksheet; // 写入表头 for (int i 0; i reportDt.Columns.Count; i) worksheet.Cells[1, i 1] reportDt.Columns[i].ColumnName; // 写入数据 for (int i 0; i reportDt.Rows.Count; i) for (int j 0; j reportDt.Columns.Count; j) worksheet.Cells[i 2, j 1] reportDt.Rows[i][j].ToString(); worksheet.Columns.AutoFit(); excelApp.Visible true; }关键点需在项目引用中添加Microsoft.Office.Interop.Excel.NET Framework 4.7.2且目标机器必须安装 Excel。若需免 Office 依赖可改用EPPlus库生成.xlsx。5.3 用 Timer BackgroundWorker 解决 C# 循环数据采集和 UI 刷新卡顿当前timerRefresh在 UI 线程执行Fill()大数据量时界面冻结。改造为后台线程private BackgroundWorker bgWorker; private void InitializeBackgroundWorker() { bgWorker new BackgroundWorker(); bgWorker.DoWork BgWorker_DoWork; bgWorker.RunWorkerCompleted BgWorker_RunWorkerCompleted; } private void BgWorker_DoWork(object sender, DoWorkEventArgs e) { // 在后台线程执行耗时查询 string sql SELECT * FROM Rooms; SqlDataAdapter adapter new SqlDataAdapter(sql, conn); DataTable dt new DataTable(); adapter.Fill(dt); e.Result dt; // 传递给 Completed 事件 } private void BgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error null) { dgvRooms.DataSource e.Result as DataTable; Console.WriteLine($后台刷新完成共 {((DataTable)e.Result).Rows.Count} 条记录); } } // 启动后台刷新 private void timerRefresh_Tick(object sender, EventArgs e) { if (!bgWorker.IsBusy) bgWorker.RunWorkerAsync(); }效果UI 线程完全不阻塞即使Fill()耗时 2 秒滚动条仍可拖动。BackgroundWorker是 .NET Framework 下最轻量的线程方案无需async/await复杂语法。本文还有配套的精品资源点击获取