简介:这是一份基于C#与SQL Server 2005开发的酒店客房管理系统课程设计完整资源,面向计算机相关专业学生及需要完成类似管理信息系统课题的开发者。系统覆盖客房管理、客人管理、预订查询、物品管理、员工设置、入住退房等核心业务环节,并附带可直接运行的源码、数据库文件及项目报告,便于理解分层设计与数据表关系。资源包共369个文件,以cs源码文件为主,辅以resx资源文件、xsd数据集定义、dll运行库、exe程序及mdf/ldf数据库文件,配置文件和项目文档也已齐备,整体约3.08MB,结构清晰、易于按模块查阅。目前已有37人学习下载。压缩包内还包含设计报告和数据库脚本,适合用于课程设计参考、功能扩展练习或毕业设计预研,尤其能帮助读者快速搭建酒店管理系统的可用原型,节省从零编码与建库的时间。
1. 基于C#的酒店客房管理系统:这个课设资源是拿来直接跑的吗
你刚拿到一个名为“课程设计-基于C#的酒店客房管理系统(源码+数据库+报告).zip”的压缩包,解压后看到的不是PPT讲稿,而是一堆Visual Studio项目文件:.bmp图标、.csproj工程、.cache编译缓存,还有两个命名看起来很怪的“宾馆管理DataSet17.Designer.cs”和“宾馆管理DataSet18.Designer.cs”。很多人第一反应是“这源码是不是坏了一半”。别急,这套东西本身是完整的,它是一份用C# WinForms写的酒店客房管理系统课程设计,带SQL Server 2005数据库脚本和配套报告文档。能做登录、用户添加、房间管理、房间类型管理、物品管理、预订查询、入住退房、员工设置等一整套功能,覆盖了“数据库增删改查”的完整链路。适合正在赶课设、又不想从零造轮子的人,也适合想快速搞懂C#连接数据库做管理系统的入门者。下面我按拆包、初始化、编译、排错的顺序,把这套资源彻底盘一遍。
2. 先理清功能与数据表:七个模块怎么对应到一张张表
2.1 从摘要功能反推数据库表结构
摘要描述里列出了十几个功能点,但拆到数据库层面,其实就是几张带外键的表互相引用。我做课设时习惯先列功能再建表:用户管理需要一张用户表,房间管理需要房间表加房间类型表,客户入住和退房需要客人表和入住记录表,预订查询需要预订表,物品管理需要物品表,破损物品查询需要破损物品表,出租方式管理需要出租方式表。
典型的表结构大概长这样:用户表Users(UserID, UserName, Password, Role),Role字段用来区分管理员和普通员工;房间类型表RoomType(RoomTypeID, TypeName, Price);房间表Room(RoomID, RoomNo, RoomTypeID, Status, Price),Status字段存“空闲/入住/预订/维修”;客人表Guest(GuestID, GuestName, IDCard, Phone);预订表Booking(BookingID, GuestID, RoomID, BeginDate, EndDate);入住记录表Record(RecordID, GuestID, RoomID, CheckInTime, CheckOutTime, Cost)。你拿到的“宾馆管理DataSet17”和“宾馆管理DataSet18”大概率就是围绕这些表生成的强类型数据集。
这里有个识别技巧:打开项目资源管理器,展开DataSet设计器,能看到TableAdapter和DataTable的名字。如果某个表适配器叫RoomTableAdapter,那对应的就是房间表。如果DataSet版本有两个(17和18),多半是之前同学迭代时留下的旧文件,打开项目时注意看最终被窗体引用的是哪一个,别认错。
2.2 项目文件里的关键代码位置:DataSet、Designer和app.config
看到.Designer.cs这种文件,新手会以为要手改,实际上那是VS可视化设计器自动生成的代码。比如“宾馆管理DataSet17.Designer.cs”里包含强类型DataSet的类定义,定义了每张表的列属性和约束。你手动改字段名,下次重新打开设计器就又被覆盖了,纯属白费力气。要改表结构,更稳妥的做法是直接在SQL Server里改表,然后回到VS里用“配置数据源”重新生成数据集。
app.config是另一个关键文件。WinForms项目的连接字符串通常写在这里的 节点里,也有写死在Properties/Settings.settings里的。如果你打开项目找不到app.config,就找Settings.settings,位置在VS解决方案资源管理器里。连接串格式一般是:
<connectionStrings> <add name="宾馆管理.Properties.Settings.HotelDBConnectionString" connectionString="Data Source=.;Initial Catalog=HotelManage;User ID=sa;Password=123456" providerName="System.Data.SqlClient" /> </connectionStrings>这个name是不固定的,取决于项目里的设置类名称。你真正要改的是connectionString属性里的Data Source和Password。另外项目里那些.bmp文件是窗体按钮的背景图,Designer.cs里会通过resources调用它们,所以别手贱去删,删了编译直接报错。
2.3 运行环境与连接串配置:SQL Server 2005还是更高版本
这套系统的开发工具是C#和SQL Server 2005,但那不意味着你必须装一个2005。SQL Server 2008、2012、2014、2016、2019、2022都能兼容这种简单的数据库,因为底层用的还是TDS协议。唯一要注意的是连接字符串写法不一样,2005的默认实例可以直接写成“Data Source=.”,而命名实例得写成“Data Source=.\SQLEXPRESS”这种形式。
如果你本机装的是Express版,默认实例名一般是“localhost\SQLEXPRESS”,数据库文件路径也要注意权限。改连接串有个原则:全局只改一处。我一般直接改app.config,这样所有窗体共享同一个连接串。除非某个DataSet的设计器里单独设置了链接属性,那种情况只能逐一检查。
连接不上时,先别怀疑代码,按这个顺序排查:1. SQL Server服务有没有启动;2. 登录名用的是不是sa,密码对不对;3. SQL Server是否开启了“SQL Server和Windows身份验证模式”。这三样是“用户 sa 登录失败”错误的最常见原因。确认无误后,再把连接字符串复制到SSMS里填一遍,如果能连上数据库,代码里就一定能连上。
3. 数据库初始化三步走:把表和测试数据装进本地SQL Server
3.1 还原数据库或执行SQL脚本
资源包里如果附带的是.sql文件,打开SQL Server Management Studio,选择目标服务器,新建一个查询窗口,把脚本整体粘贴进去执行。执行前先看一眼脚本开头有没有CREATE DATABASE语句,如果有,脚本会自动建库;如果没有,你得先手动建一个库,再把USE语句改成你的库名。执行完以后,在对象资源管理器里刷新,看到库和表就算成功。
如果附带的是.mdf和.ldf文件,就需要用“附加数据库”方式。右键“数据库”节点,选择“附加”,点击“添加”,选中的mdf文件路径里不能有中文,否则会失败。附加后数据库名默认就是mdf的逻辑名,如果和连接字符串里的Initial Catalog不一致,右键数据库“重命名”改成一致。
我习惯用.sql方案,因为能看清楚建表顺序。给你看一个最小建表脚本片段:
USE HotelManage; GO CREATE TABLE dbo.RoomType( RoomTypeID INT IDENTITY(1,1) PRIMARY KEY, TypeName NVarChar(50) NOT NULL, Price Decimal(10,2) NOT NULL ); GO CREATE TABLE dbo.Room( RoomID INT IDENTITY(1,1) PRIMARY KEY, RoomNo NVarChar(10) NOT NULL, RoomTypeID INT NOT NULL, Status NVarChar(20) DEFAULT '空闲', Price Decimal(10,2) NOT NULL, CONSTRAINT FK_Room_RoomType FOREIGN KEY(RoomTypeID) REFERENCES dbo.RoomType(RoomTypeID) );这里IDENTITY(1,1)表示自增列,起始值是1,步长是1,不用手动插入RoomTypeID;NVarChar(50)表示可存储50个字符的Unicode字符串,专门配合中文字段。外键约束保证Room里的RoomTypeID必须存在于RoomType表,防止录入错误数据。
3.2 登录模块的实现逻辑:SQL注入要当心
登录界面是这个项目最先被打开测试的地方,也是最容易出低级问题的位置。很多课设代码喜欢直接拼SQL字符串去验证用户名密码,比如:
string sql = "SELECT * FROM Users WHERE UserName='" + txtUser.Text + "' AND Password='" + txtPwd.Text + "'";这条代码在单机课设里可能跑得通,但只要你把输入框内容改成“' OR '1'='1”,SQL就变成:
SELECT * FROM Users WHERE UserName='' OR '1'='1' AND Password=''因为OR后面恒为真,整个表就泄露了。这就是最典型的SQL注入。好的项目会用参数化查询,比如用SqlCommand的Parameters集合。你拿到项目的源码后,可以全局搜索“String.Format”和“+”,凡是发现拼SQL的地方,建议全部改成参数化。下面是改法示例:
string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["HotelDB"].ConnectionString; using (SqlConnection conn = new SqlConnection(connStr)) { string sql = "SELECT Role FROM Users WHERE UserName=@name AND Password=@pwd"; SqlCommand cmd = new SqlCommand(sql, conn); cmd.Parameters.AddWithValue("@name", txtUserName.Text.Trim()); cmd.Parameters.AddWithValue("@pwd", txtPassword.Text); conn.Open(); object result = cmd.ExecuteScalar(); if (result != null) { Global.CurrentUser = txtUserName.Text.Trim(); MessageBox.Show("登录成功,角色:" + result.ToString()); } }注意这里用了using语句,确保SqlConnection在结束后自动释放,避免连接数堆积。@name和@pwd是参数占位符,AddWithValue会把这俩值转成数据库类型,传入的引号仅仅作为字符存在,不会再参与SQL语法解析,从根上堵住注入漏洞。
3.3 房间管理、预订、入住退房的数据流转
这套系统的核心业务是房间状态流转。房间表有个Status字段,常见值有“空闲”、“入住中”、“预订中”、“维修中”。你入住时,要先向Guest表插入客人信息,拿到GuestID;再向Record表插入入住记录,把Room.Status改成“入住中”。退房时,根据RecordID更新CheckOutTime和Cost,再把Room.Status改回“空闲”。预订则只改Status为“预订中”,不写Record。
这个流程如果用多条SQL一句一句执行,中途失败就会出现脏数据。比如插入客人记录成功了,但更新房间状态时网络断了,结果房间还是“空闲”,但系统里多了一个没有对应房间的入住记录。解决方式是使用事务:
using (SqlConnection conn = new SqlConnection(connStr)) { conn.Open(); SqlTransaction tran = conn.BeginTransaction(); try { SqlCommand cmdGuest = new SqlCommand("INSERT INTO Guest(GuestName,IDCard,Phone) OUTPUT INSERTED.GuestID VALUES(@g,@i,@p)", conn, tran); cmdGuest.Parameters.AddWithValue("@g", txtGuestName.Text.Trim()); cmdGuest.Parameters.AddWithValue("@i", txtIDCard.Text.Trim()); cmdGuest.Parameters.AddWithValue("@p", txtPhone.Text.Trim()); int guestId = (int)cmdGuest.ExecuteScalar(); SqlCommand cmdRecord = new SqlCommand("INSERT INTO Record(GuestID,RoomID,CheckInTime) VALUES(@g,@r,@t)", conn, tran); cmdRecord.Parameters.AddWithValue("@g", guestId); cmdRecord.Parameters.AddWithValue("@r", selectedRoomId); cmdRecord.Parameters.AddWithValue("@t", DateTime.Now); cmdRecord.ExecuteNonQuery(); SqlCommand cmdRoom = new SqlCommand("UPDATE Room SET Status='入住中' WHERE RoomID=@r", conn, tran); cmdRoom.Parameters.AddWithValue("@r", selectedRoomId); cmdRoom.ExecuteNonQuery(); tran.Commit(); } catch { tran.Rollback(); throw; } }这段代码的关键在于把三条命令绑定到同一个SqlTransaction上,任何一个步骤报错,回滚后会恢复到事务开始前的状态。注意OUTPUT INSERTED.GuestID直接取自增ID,比先Select MAX(GuestID)再插入靠谱得多,因为并发下MAX可能拿到别人的值。如果原来的项目代码里用Select MAX,建议顺手改掉。
4. 把Visual Studio项目跑起来:打开、编译、连接串修改一条龙
4.1 打开项目前的准备:.NET版本和引用
这个课程设计是VS 2005/2008时代的产物,目标框架很可能是.NET Framework 2.0或3.5。你用VS 2019/2022双击.sln时,会弹出一个“需要升级项目”的提示。可以点确定,也可以直接打开.csproj让VS自动转换。转换后要注意两件事:第一,旧版C#语法可能在高版本编译器中不兼容,比如某些控件属性被废弃;第二,系统会自动添加一批新的引用来适配,比如Microsoft.CSharp。
转换完成后先编译一次,而不是先运行。如果看到“无法找到类型或命名空间名”,通常是缺using或项目引用。WinForms项目最常用的引用是System.Windows.Forms、System.Drawing、System.Data、System.Configuration。检查一下“项目”->“添加引用”里这些是否都在。另外,ReportViewer是个特殊控件,如果你没装对应的报表工具包,会出现“无法加载Microsoft.ReportViewer.WinForms”之类错误,那就要去NuGet包管理里搜索“ReportViewer”安装,或者直接卸载报表相关代码。
4.2 数据库连接字符串修改
项目里至少有app.config和两个DataSet设计器,它们都可能包含连接串,但最终生效的是运行时读取的那个。打开app.config,定位到connectionStrings节点。常见的错误写法是直接把本机SQL Server信息写死到代码里,比如“Data Source=localhost; Initial Catalog=数据库名; User ID=sa; Password=123456”。这个写法在开发机可能能用,但要上台演示换一台电脑,就会卡死。正确做法是只改app.config,代码里统一读取。
修改时注意几个细节:Data Source可以用“.”表示本机默认实例,如果用命名实例要写“名字\实例名”;Initial Catalog要和数据库名字一致;User ID和Password要和你SQL Server登录账号一致。改完后,用下面这段代码检查连接串能不能被正确读取:
string connStr = ConfigurationManager.ConnectionStrings["HotelDB"].ConnectionString; using (SqlConnection conn = new SqlConnection(connStr)) { try { conn.Open(); MessageBox.Show("连接成功"); } catch (Exception ex) { MessageBox.Show("连接失败:" + ex.Message); } }如果这里弹窗显示“连接失败”,先回去检查SQL Server服务是否开启,然后看错误消息里有几个关键词。“用户sa登录失败”说明账号密码错,需要去SSMS里改;如果报“找不到服务器”,说明Data Source写错。改完后,重新生成一次DataSet,让TableAdapter重新加载连接串。具体做法是:打开DataSet设计器,右键某个TableAdapter,选择“配置”,向导里会显示连接串,点下一步直到完成。
提示:修改连接串后一定要重新生成数据集,否则编译时Designer.cs里缓存的旧连接串会覆盖你的配置。
4.3 编译报错的排查顺序
我兼职帮人救过不少课设项目,发现大家面对编译错误时最容易犯的毛病是盯着最后一条错误看,而忽略第一条。编译错误是有因果关系的,第一条错误往往是根源,后面的都是它的连带。比如“无法找到类型或命名空间名 X”,通常是因为前面的一个using写错了;而“未能加载文件或程序集”开头的错误,才是引用层面的问题。
一个标准排查顺序:首先看错误列表里有没有红色波浪线的文件,双击跳过去,看具体哪一行。先解决带“CS”开头的错误,比如CS0246是找不到类型,CS1002是缺分号。其次解决“生成”错误,这类错误往往能定位到具体dll。最后再处理警告,警告不影响运行,可以先忽略。
如果错误列表里出现与DataSet设计器相关的错误,比如“无法连接到数据库”或者“对象引用未设置到实例”,先不要慌,按前面说的方法检查连接串和SQL Server服务。有时候光改app.config还不够,因为DataSet设计器内部缓存了连接串,重新生成便好。
5. 避坑指南:课设项目里的五个典型翻车现场
5.1 现象:双击.sln没反应,或者提示项目版本太旧
原因:项目文件使用了旧版VS格式,高版本VS默认不认。 解决:用VS打开.csproj文件,选择“打开项目”;如果提示升级,确认备份后点升级。升级后如果编译报“不支持的关键字”之类的错,先把项目目标框架改成.NET Framework 4.x再编译。
5.2 现象:登录按钮一点,报“未将对象引用设置到对象的实例”
原因:登录窗体里用了DataSet对象,但没实例化;或者TableAdapter没有填充数据。 解决:在窗体构造函数里实例化DataSet,比如“this.dataSet17 = new DataSet17();”,然后在登录按钮事件中先调用对应的TableAdapter.Fill填充数据。如果代码是手写的,检查SqlConnection和SqlCommand是否都new了,是不是漏了连接串赋值。
5.3 现象:报表预览是白纸,没有任何数据
原因:报表的数据源没有绑定,或者数据集的表是空的。 解决:在报表窗体的Load事件里,先填充表,再设置ReportDataSource。常见代码是:
this.rptRecordTableAdapter.Fill(this.宾馆管理DataSet18.Record); ReportDataSource rds = new ReportDataSource("Record", this.宾馆管理DataSet18.Record); this.reportViewer1.LocalReport.DataSources.Clear(); this.reportViewer1.LocalReport.DataSources.Add(rds); this.reportViewer1.RefreshReport();注意“Record”名称要和报表里定义的数据集名称完全一致,大小写敏感。如果报表没有数据,拿断点看Fill执行完后DataSet18.Record的行数,如果为0,说明查询条件或者表名不对。
5.4 现象:退房结算时房间状态莫名变成“无”或空字符串
原因:UPDATE语句带了不必要的条件,或没有WHERE限定;也可能是绑定到窗体的DataTable忘了更新,界面显示的是旧数据。 解决:在退房事务中明确写UPDATE Room SET Status='空闲' WHERE RoomID=@rid AND Status='入住中'。执行完UPDATE后,重新查询一遍Room表并刷新DataGridView,不要只改数据库不刷新UI。
5.5 现象:修改窗体代码后,发现一保存就把窗口设计搞乱了
原因:直接手动改Designer.cs文件,引入不能解析的资源或事件。 解决:不要手改Designer.cs。要改窗体的Title、位置、大小,就用VS设计器改;要动态添加按钮,就在窗体构造函数里写代码,而不是往Designer.cs里塞。如果已经改坏了,用git或文件备份还原,没有备份就重开一个窗体,把旧窗体的代码复制过去。
6. 进阶技巧:把课设改造成答辩能过的毕设
6.1 加一个操作日志表
给系统加一个OperateLog表,每次用户入住、退房、修改房间价格,都记录操作人、操作内容、操作时间。这会让答辩老师觉得你有安全意识。
CREATE TABLE OperateLog( LogID INT IDENTITY(1,1) PRIMARY KEY, UserName NVarChar(50), Operation NVarChar(200), OperateTime DateTime DEFAULT GETDATE() );然后在C#里写一个公共方法:
public static void WriteLog(string userName, string operation) { using (SqlConnection conn = new SqlConnection(connStr)) { SqlCommand cmd = new SqlCommand("INSERT INTO OperateLog(UserName,Operation) VALUES(@u,@o)", conn); cmd.Parameters.AddWithValue("@u", userName); cmd.Parameters.AddWithValue("@o", operation); conn.Open(); cmd.ExecuteNonQuery(); } }6.2 用DataGridView的单元格事件增强交互
默认的课设界面可能是DataGridView只读,用户要双击才弹出编辑框。我们用CellDoubleClick事件把当前行的数据自动填到右边文本框,演示流畅度会好很多。
private void dgvRooms_CellDoubleClick(object sender, DataGridViewCellEventArgs e) { if (e.RowIndex < 0) return; DataGridViewRow row = dgvRooms.Rows[e.RowIndex]; txtRoomNo.Text = row.Cells["RoomNo"].Value.ToString(); cboRoomType.SelectedValue = row.Cells["RoomTypeID"].Value; txtPrice.Text = row.Cells["Price"].Value.ToString(); cboStatus.SelectedItem = row.Cells["Status"].Value.ToString(); }注意行索引为-1表示点击了列头,必须过滤。如果单元格值是DBNull,调用ToString时也要做空值判断,否则会报“不能将类型DBNull转换为String”。
6.3 验证清单:演示前必做的五件事
答辩演示最怕在台上翻车。我习惯提前按下面清单走一遍:1. 先登录管理员账号,确认进去之后菜单和角色权限正常;2. 新增一个房间,再删除它,看列表刷新是否正常;3. 模拟客户入住-退房完整流程,确认房间状态从“空闲”变“入住中”再变“空闲”,并且记录表里多了一条退房记录;4. 搜索预订记录,确认查询条件能正确拼接;5. 关闭数据库服务一次,再重启,确认连接字符串扛得住。
这五件事里第三件是最容易出问题的,因为涉及事务和多表联动。你可以在演示前一天把数据库做一次完整备份,万一现场搞乱了,用备份恢复就能救场。从那以后我每次帮人看课设,都会先跑一遍入住和退房流程,确认状态字段没脏数据,再让对方上答辩。数据库连接串、登录权限、房间状态这三样是答辩高频翻车点,你提前踩完,演示就顺了。希望帮到你。
本文还有配套的精品资源,点击获取