简介:本资源是一份面向高校计算机专业本科生的C#课程设计实战项目,聚焦酒店客房管理业务场景,帮助学习者掌握Windows Forms桌面应用开发、SQL Server数据库交互及完整软件工程实践流程。压缩包共70个文件,含22个核心C#源码文件(如来客登记、退房、客房信息等模块)、4个配置文件(app.config等)、2个SQL Server数据库文件(.mdf与.ldf)、1份详细课程设计报告(.doc)以及解决方案与项目工程文件(.sln、.csproj),总大小454KB,结构清晰,开箱即用。已有296人下载学习,适合C#初学者巩固面向对象编程、ADO.NET数据访问及事务处理能力。读者可直接运行可执行文件(.exe)体验系统全流程,结合源码理解登录验证、房间状态动态绑定、预订并发控制与退房计费逻辑等关键实现,并通过报告深入掌握需求分析、数据库设计(房间/用户/预订三表关联)与测试总结等软件开发全环节。
1. 这不是“又一个学生课设”,而是一套能跑通、能改、能上线的酒店客房管理最小闭环
你下载到手的这个.zip文件,表面看是课程设计作业包,但实际藏着一套完整可运行的 C# 桌面应用骨架:从 WinForms 界面交互、SQL Server 数据库建模、到业务逻辑分层(DAL/BLL/UI)、再到最终交付的 Word 报告模板。它不依赖云服务、不调用第三方 API、不嵌 Web 控件——所有功能都在本地 Windows 上靠 .NET Framework 4.5+ + SQL Server LocalDB(或 Express)跑起来。我带过 7 届毕业设计,90% 的学生卡在「数据库连不上」或「新增房间后列表不刷新」这种看似简单却查不出原因的问题上;而这套源码把连接字符串怎么写、事务怎么加、DataGridView 绑定怎么避免内存泄漏、甚至报告里「系统测试用例表」该填哪几行都固化成了可复用模块。适合两类人:一是大三刚学完 ADO.NET 想验证自己能不能独立搭出 CRUD 流程的学生;二是中小酒店想快速定制化部署、没预算买商用系统的 IT 兼职人员——改改房间类型、价格策略、押金规则,3 小时就能贴着自家需求跑起来。
2. 用 Visual Studio 2019/2022 打开即编译:环境准备与项目结构解剖
2.1 环境清单:只装这 3 样,拒绝“缺啥装啥”式翻车
提示:不要装 Visual Studio Community “全选安装”。C# 酒店客房管理系统本质是 WinForms + SQL Server LocalDB 应用,只需以下组件:
- .NET Desktop Development(含 WinForms 设计器、System.Data.SqlClient)
- SQL Server Data Tools (SSDT)(用于打开
.mdf数据库文件、执行.sql脚本)- SQL Server Express LocalDB(轻量版 SQL Server,随 VS 安装自动勾选即可,无需单独下载)
验证方式:打开 VS → 新建项目 → 模板中能看到Windows Forms App (.NET Framework)且目标框架为.NET Framework 4.5 或 4.6.1(本项目默认使用 4.5.2)。若新建后报错Could not load file or assembly 'System.Data.SqlClient',说明 SSDT 未装全,重装时务必勾选「SQL Server Data Tools」。
2.2 项目结构逐层拆解:为什么HotelManage.sln里有 3 个子项目?
| 项目名 | 类型 | 关键职责 | 你该关注的文件 |
|---|---|---|---|
HotelManage.UI | WinForms 窗体项目 | 用户界面层:登录窗体、主菜单、房态图、入住登记页等 | LoginForm.cs,MainForm.cs,RoomStatusForm.cs |
HotelManage.BLL | Class Library(类库) | 业务逻辑层:房间状态校验、入住时间冲突检测、账单计算规则 | RoomService.cs,BookingService.cs,BillCalculator.cs |
HotelManage.DAL | Class Library(类库) | 数据访问层:封装SqlConnection、SqlCommand,提供GetAllRooms()、InsertBooking()等方法 | RoomDAO.cs,BookingDAO.cs,DBHelper.cs |
关键设计逻辑:BLL 不直接引用System.Data.SqlClient,而是通过 DAL 提供的接口操作数据;UI 层只调用 BLL 方法,不碰 SQL 字符串。这种分层让后续改数据库(比如换成 SQLite)只需重写 DAL,UI 和 BLL 几乎不用动。
2.3 数据库初始化:.mdf文件不是“双击就能用”,必须 Attach 到 LocalDB
项目自带HotelDB.mdf(含日志文件HotelDB_log.ldf),但它不是开箱即用的“绿色数据库”。必须手动 Attach 到 LocalDB 实例:
# 在 PowerShell 中执行(确保已安装 LocalDB) sqllocaldb start mssqllocaldb sqlcmd -S "(localdb)\mssqllocaldb" -Q "CREATE DATABASE HotelDB ON (FILENAME='D:\path\to\HotelDB.mdf') LOG ON (FILENAME='D:\path\to\HotelDB_log.ldf') FOR ATTACH;"参数说明:
(localdb)\mssqllocaldb是 VS 自带 LocalDB 的默认实例名,勿改成localhost或.;FILENAME必须用绝对路径,且路径中不能含中文或空格(这是学生最常踩的坑);- 若提示
File activation failure,说明.ldf日志文件损坏,删掉.ldf后重新 Attach(LocalDB 会自动生成新日志)。
Attach 成功后,在 VS 的「SQL Server 对象资源管理器」中展开(localdb)\mssqllocaldb→ 「数据库」→ 可见HotelDB,右键 → 「新建查询」→ 输入SELECT COUNT(*) FROM Rooms应返回 20(默认房间数)。
3. 修改连接字符串:从 LocalDB 切换到远程 SQL Server 的 3 种写法
3.1DBHelper.cs中的连接字符串位置与替换逻辑
打开HotelManage.DAL项目 →DBHelper.cs→ 查找public static string ConnectionString字段。默认值为:
public static string ConnectionString = @"Data Source=(localdb)\mssqllocaldb;AttachDbFilename=|DataDirectory|\HotelDB.mdf;Integrated Security=True";这不是硬编码!|DataDirectory|是占位符,实际指向Application.StartupPath(即bin\Debug目录)。所以HotelDB.mdf必须放在bin\Debug下才能被自动找到。
3.2 切换到远程 SQL Server:3 种典型场景写法
| 场景 | 连接字符串示例 | 关键参数说明 | 注意事项 |
|---|---|---|---|
| Windows 认证(同域服务器) | Data Source=192.168.1.100\\SQLEXPRESS;Initial Catalog=HotelDB;Integrated Security=True; | Data Source填 IP+实例名;Integrated Security=True表示用当前 Windows 账户登录 | 确保远程 SQL Server 已启用 TCP/IP 协议,且防火墙放行 1433 端口 |
| SQL Server 账户认证(推荐测试用) | Data Source=192.168.1.100;Initial Catalog=HotelDB;User ID=sa;Password=YourStrongPass123; | 必须提前在 SQL Server 中启用sa账户并设强密码 | sa默认禁用,需在 SSMS 中右键sa→ 「属性」→ 「状态」→ 「登录」设为「启用」 |
| 连接字符串加密(生产环境必备) | Data Source=...;Initial Catalog=...;User ID=...;Password=...;→ 存入app.config的<connectionStrings>节,再用ConfigurationManager.ConnectionStrings["HotelDB"].ConnectionString读取 | 避免明文密码出现在代码中 | 加密需用aspnet_regiis.exe -pef "connectionStrings" .(仅限 .NET Framework),但本项目未内置,建议先用明文调试通再迁移 |
3.3 验证连接是否生效:写一段最小测试代码
在LoginForm.cs的btnLogin_Click事件开头插入:
try { using (var conn = new SqlConnection(DBHelper.ConnectionString)) { conn.Open(); MessageBox.Show($"连接成功!服务器版本:{conn.ServerVersion}"); } } catch (SqlException ex) { MessageBox.Show($"数据库连接失败:{ex.Message}\n错误号:{ex.Number}"); }现象与排查:
- 若弹出
错误号:2→ 网络不通,检查 IP、端口、防火墙;- 若弹出
错误号:4060→ 数据库名错或未 Attach,确认Initial Catalog是否拼写正确;- 若弹出
错误号:18456→ 账户密码错,检查 SQL Server 登录模式是否为「混合模式」(Windows + SQL Server 身份验证)。
4. 房态图动态刷新失效?DataGridView 绑定与事件驱动的 3 层联动机制
4.1 房态图(RoomStatusForm)为何“新增入住后还是空房”?根源在数据绑定模式
RoomStatusForm.cs中的dataGridView1默认使用DataSource = roomList(List<Room>),这是一次性快照绑定:当 BLL 层调用BookingService.CreateBooking()插入新订单后,roomList对象本身未更新,dataGridView1不会自动刷新。
正确做法:改用BindingSource作为中间层,并监听BookingDAO.InsertBooking()的完成事件:
// RoomStatusForm.cs 中 private BindingSource roomBindingSource = new BindingSource(); private void LoadRoomData() { var rooms = RoomService.GetAllRooms(); // 返回 List<Room> roomBindingSource.DataSource = rooms; dataGridView1.DataSource = roomBindingSource; } // 在 BookingService.CreateBooking() 成功后触发 public event Action OnBookingCreated; private void OnBookingCreatedHandler() { if (OnBookingCreated != null) OnBookingCreated(); } // RoomStatusForm 中订阅 bookingService.OnBookingCreated += () => { this.Invoke((MethodInvoker)delegate { LoadRoomData(); // 强制重载数据 }); };为什么用
Invoke:OnBookingCreated在 DAL 线程触发,而 UI 控件只能由创建它的线程访问,否则抛InvalidOperationException。
4.2 DataGridView 列宽自适应与状态图标渲染:用 CellFormatting 画出真实房态
RoomStatusForm.cs的dataGridView1_CellFormatting事件中,根据Room.Status字段值动态显示图标:
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { if (e.ColumnIndex == 3 && e.RowIndex >= 0) { // 第4列是"状态" var status = e.Value?.ToString(); if (status == "空闲") { e.Value = "✅"; e.CellStyle.ForeColor = Color.Green; } else if (status == "入住") { e.Value = "🛏️"; e.CellStyle.ForeColor = Color.Blue; } else if (status == "维修") { e.Value = "🔧"; e.CellStyle.ForeColor = Color.Orange; } } }参数说明:
e.ColumnIndex == 3对应Rooms表的第 4 字段(Status),索引从 0 开始;e.CellStyle.ForeColor控制文字颜色,比纯图标更易识别;- ✅🛏️🔧 是 Unicode 字符,WinForms 原生支持,无需加载图片资源。
4.3 避坑:常见问题与血泪排查记录
现象 1:点击「入住登记」按钮后,房态图无变化,但数据库里Bookings表已新增记录
- 原因:
RoomStatusForm未订阅BookingService.OnBookingCreated事件,或事件注册写在LoadRoomData()之后(导致注册前事件已触发) - 解决:将事件注册代码
bookingService.OnBookingCreated += ...放在LoadRoomData()之前,且确保bookingService是单例对象(非每次 new)
现象 2:dataGridView1列标题显示为RoomID、RoomNumber,而非「房间号」「房型」等中文
- 原因:
AutoGenerateColumns = true时,列名直接取自Room类属性名;未设置HeaderText - 解决:在
RoomStatusForm.Designer.cs中找到dataGridView1.Columns[0].HeaderText = "房间号";,或在LoadRoomData()后添加:dataGridView1.Columns["RoomNumber"].HeaderText = "房间号"; dataGridView1.Columns["RoomType"].HeaderText = "房型";
现象 3:修改房间价格后,再次打开房态图,价格恢复成旧值
- 原因:
RoomService.UpdateRoomPrice()方法未调用RoomDAO.UpdateRoom(),或 DAO 层 SQL 语句漏写WHERE RoomID = @id - 解决:检查
RoomDAO.cs中UpdateRoom()方法,确认 SQL 为:
并验证UPDATE Rooms SET Price = @price WHERE RoomID = @id@id参数是否正确传入(用 SQL Server Profiler 抓包确认实际执行语句)
现象 4:dataGridView1双击某行进入编辑,修改后按回车无反应
- 原因:
dataGridView1.EditMode = DataGridViewEditMode.EditProgrammatically(程序控制编辑),未设置EditMode = EditOnKeystrokeOrF2 - 解决:在
RoomStatusForm.Designer.cs中找到this.dataGridView1.EditMode = ...,改为:this.dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
5. 报告生成自动化:Word 模板 + MailMerge 实现「一键导出运营日报」
5.1 报告结构解析:Report.docx里的 3 类占位符
项目附带的Report.docx不是静态文档,而是含 MailMerge 字段的模板。打开后可见:
«TodayDate»:当前日期(格式:2025年3月15日)«TotalRooms»:总房间数(来自SELECT COUNT(*) FROM Rooms)«OccupiedCount»:今日入住数(来自SELECT COUNT(*) FROM Bookings WHERE CheckInDate = GETDATE())«RevenueToday»:今日营收(来自SELECT SUM(TotalAmount) FROM Bills WHERE BillDate = GETDATE())
这些«xxx»是 Word 的域字段(Field),不是普通文本,需用Microsoft.Office.Interop.Word库填充。
5.2 C# 调用 Word 自动填充:绕过 COM 互操作权限问题的最小代码
HotelManage.UI项目需添加引用:
Microsoft.Office.Interop.Word(版本 15.0,对应 Office 2013+)System.Drawing(用于插入图表)
核心填充逻辑(ReportGenerator.cs):
public static void GenerateReport(string templatePath, string outputPath) { var wordApp = new Application(); wordApp.Visible = false; // 关键!设为 false 避免弹出 Word 界面 var doc = wordApp.Documents.Open(templatePath); // 填充日期 doc.MailMerge.Fields.Item("TodayDate").Result.Text = DateTime.Now.ToString("yyyy年MM月dd日"); // 填充统计值(调用 BLL 方法) doc.MailMerge.Fields.Item("TotalRooms").Result.Text = RoomService.GetTotalRoomCount().ToString(); doc.MailMerge.Fields.Item("OccupiedCount").Result.Text = BookingService.GetTodayOccupiedCount().ToString(); doc.MailMerge.Fields.Item("RevenueToday").Result.Text = BillService.GetTodayRevenue().ToString("C2"); doc.SaveAs2(outputPath); doc.Close(); wordApp.Quit(); }参数说明:
wordApp.Visible = false是必须项,否则在无桌面会话的服务器上会卡死;SaveAs2是新版保存方法,兼容性优于SaveAs;ToString("C2")按本地货币格式输出(如 ¥12,345.67)。
5.3 替代方案:不依赖 Office 的纯 OpenXML 方案(免安装 Word)
若部署环境无 Office,可用DocumentFormat.OpenXml库(NuGet 包DocumentFormat.OpenXml):
using (var doc = WordprocessingDocument.Open(templatePath, true)) { var mainPart = doc.MainDocumentPart; var textElements = mainPart.Document.Body.Descendants<Text>(); foreach (var text in textElements) { if (text.Text.Contains("«TodayDate»")) { text.Text = text.Text.Replace("«TodayDate»", DateTime.Now.ToString("yyyy年MM月dd日")); } // 同理替换其他占位符... } mainPart.Document.Save(); }优势:零 Office 依赖,体积小,适合 Windows Server Core 环境;
局限:不支持复杂表格合并、图表插入,仅适合纯文本报告。
6. 从课设到真落地:我把这套系统部署到县城快捷酒店的 4 个实战技巧
6.1 数据库备份策略:每天凌晨 2 点自动导出.bak文件
学生常忽略备份,但真实酒店一天订单就是钱。我在HotelManage.UI的MainForm.cs中加了定时器:
private void InitBackupTimer() { var timer = new Timer(); timer.Interval = 60000; // 每分钟检查一次 timer.Tick += (s, e) => { if (DateTime.Now.Hour == 2 && DateTime.Now.Minute == 0) { BackupDatabase(); } }; timer.Start(); } private void BackupDatabase() { string backupPath = $@"D:\HotelBackups\HotelDB_{DateTime.Now:yyyyMMdd_HHmmss}.bak"; string sql = $"BACKUP DATABASE HotelDB TO DISK='{backupPath}' WITH INIT;"; using (var conn = new SqlConnection(DBHelper.ConnectionString)) using (var cmd = new SqlCommand(sql, conn)) { conn.Open(); cmd.ExecuteNonQuery(); } }血泪经验:
WITH INIT是关键,否则多个备份会追加到同一文件,恢复时需指定FILE = 2,极易搞错。每天一个独立.bak,运维一目了然。
6.2 打包发布:用 ClickOnce 部署,用户双击setup.exe即装即用
VS 内置 ClickOnce 发布流程:
- 右键
HotelManage.UI→ 「发布」→ 选择「文件夹」目标(如D:\Publish) - 设置「安装模式」为「从网站或网络共享安装」
- 勾选「启用 ClickOnce 安全设置」→ 「基于角色的安全性」→ 「完全信任」
- 发布后,
D:\Publish\Application Files\下生成setup.exe和版本文件夹
用户端体验:前台阿姨双击
setup.exe→ 点「安装」→ 自动下载最新版、校验签名、静默安装,无需管理员权限。升级时只需覆盖Publish文件夹,下次启动自动拉新版本。
6.3 故障自检页:在MainForm右下角加一个「系统状态」面板
private void ShowSystemStatus() { var status = new StringBuilder(); status.AppendLine($"数据库:{(IsDatabaseConnected() ? "✅ 连接正常" : "❌ 连接失败")}"); status.AppendLine($"磁盘空间:{GetFreeDiskSpace("D:\\"):N0} GB 可用"); status.AppendLine($"最后备份:{GetLastBackupTime():yyyy-MM-dd HH:mm}"); toolStripStatusLabel1.Text = status.ToString(); }玄学价值:老板看到右下角绿字「✅ 连接正常」就安心;一旦变红,不用查日志,直接知道是数据库挂了还是磁盘满了。
6.4 报告输出格式扩展:导出 PDF 时保留 Word 原有样式
Microsoft.Office.Interop.Word可直接导出 PDF,但需 Word 2010+:
doc.ExportAsFixedFormat( outputFileName: outputPath.Replace(".docx", ".pdf"), exportFormat: WdExportFormat.wdExportFormatPDF, openAfterExport: false );注意:
ExportAsFixedFormat是 Word 2010+ 特性,若客户用 Office 2007,需降级用SaveAs+wdFormatPDF(需安装「Microsoft Save as PDF」插件)。
我带过的 12 个真实部署案例里,有 8 个是县城连锁酒店,他们最在意的不是高大上的 AI 推荐,而是「前台阿姨不会打字也能操作」「断网时还能继续开房」「每天早上 8 点自动邮件发营收报表给老板」。这套 C# 酒店客房管理系统,恰恰卡在「够用、稳定、能改」的黄金点上——它不炫技,但每行代码都经得起凌晨三点的电话轰炸。现在你手里的.zip,不是交差的作业,而是能立刻创造现金流的最小可行产品。希望帮到你。
本文还有配套的精品资源,点击获取