简介:这是一套完整的C#房屋租赁管理系统课程设计资料包,面向计算机专业学生及需要完成数据库课程设计的小白开发者,帮助解决从系统设计、编码实现到报告撰写全流程的参考需求。压缩包共71个文件,大小12.86MB,内含C#窗体应用源码(.cs与.resx/.resources窗体资源)、可直接运行的.exe程序、MySQL建表脚本(.sql)、详细课程设计报告(.doc/.docx)以及ER图、数据流图等设计文档,结构清晰,代码与文档配套齐全。目前已有146人学习下载,适合对照参考。这套资料最大价值在于无需自己从零搭建,源码中涵盖房源管理、租客登记、合同签约、到期提醒等常见业务模块,数据库脚本可直接导入;报告附带的封皮模板与图示,也能帮助快速完成课程设计文档。通过阅读源码与报告,能直观掌握C#与MySQL的交互方式以及系统分层思路。
1. 房屋租赁管理系统:一个数据库课程设计该怎么拆
把“房屋租赁管理系统”当成课程设计选题时,很多人第一反应是写个 C# 窗口、连上数据库、能增删改查就交差。但最后能评上“优秀”的课程设计,拉开差距的地方往往不在窗体数量,而在数据关系设计:房屋、租客、租约、账单这几类数据怎么组织,状态怎么流转,退租时怎么把账算平。
这篇文章面向正在做 C# 数据库课程设计,以及想用一套完整系统练手 C# 基础和数据库操作的同学。按“先设计、后编码、再避坑”的顺序把一套可运行的方案讲清楚。全程用 WinForms 加原生 ADO.NET,不依赖任何框架,你在 C# 入门阶段掌握的知识就够用,照着做就能跑通。
2. 先设计数据再写窗体:房屋租赁管理系统的表结构与项目骨架
数据库课程设计的评分重点,一半在表设计得合不合理,一半在代码能不能把表之间的关系演示清楚。所以动手写第一个窗体之前,先把数据模型定下来。
2.1 数据关系先于代码:租客、房屋、租约三张表怎么连
一套不带花哨功能的房屋租赁管理系统,最少需要五张表:用户表、房屋表、租客表、租约表、账单表。租约表是核心,它把房屋和租客连起来,账单再挂在租约下。
| 表名 | 关键字段 | 状态字段 | 作用 |
|---|---|---|---|
| Users | UserId, UserName, Password, Role | 无 | 系统登录,区分管理员和操作员 |
| House | HouseId, HouseNo, Address, Area, MonthlyRent | Status(0空置/1已租/2维修) | 维护房源信息 |
| Tenant | TenantId, Name, Phone, IdCard | 无 | 租客基本信息 |
| Lease | LeaseId, HouseId, TenantId, StartDate, EndDate, MonthlyRent, Deposit | Status(1生效中/0已终止) | 记录一次租赁关系 |
| Payment | PaymentId, LeaseId, PayDate, Amount, PayMethod | 无 | 每次缴费记录 |
这里有个值得在报告里专门写一段的设计:House 表里冗余了 Status 字段,同时 Lease 表里也冗余了 MonthlyRent。为什么不实时 JOIN 算出来?因为房屋状态在界面上要频繁显示,每次动态计算会多一条关联查询;月租金冗余到租约里,是为了防止以后房价调整影响历史账单,财务上这叫“快照”。
字段类型也要养成习惯:金额用 decimal 而不是 float,身份证号用 nvarchar(18) 而不是数字类型,日期用 date 或 datetime,状态用 int 而不是 bit,因为状态不止两种。这些细节在课程设计答辩时经常被老师拎出来问,提前答得上来就是加分项。
2.2 C#项目骨架:WinForms + 三层架构的选型与取舍
很多教程一上来就让建五个项目,小白直接劝退。我的建议是:解决方案建两个项目就够——一个 WinForms 窗体项目负责界面,一个类库项目负责数据访问。业务逻辑简单,不必强行拆出 BLL,否则报告写起来费劲,代码也没多干净。
界面层只做一件事:收集用户输入、把结果显示到 DataGridView 或文本框。数据访问层只做一件事:接收界面传来的参数,执行 SQL,返回 DataTable 或实体对象。连接字符串写到 App.config 里,不硬编码在代码中,这是最基础也最容易被检查的规范。
这样一个简单的两层结构,既满足课程设计评分标准里的“分层清晰”,又不会让小白陷入过度设计。代码组织上,按窗体文件分组:LoginForm、MainForm、HouseForm、TenantForm、LeaseForm、PaymentForm,每个窗体的数据库操作都走同一个数据访问类,避免每个窗体各写一套 SqlConnection。
2.3 从零建库:SQL建表脚本与连接字符串的最小配置
数据库我用 SQL Server,这也是 C# 课程设计最常见的选择。打开 SSMS 新建数据库 RentDB,然后执行下面的建表脚本:
CREATE TABLE Users ( UserId INT IDENTITY(1,1) PRIMARY KEY, UserName NVARCHAR(50) NOT NULL UNIQUE, Password NVARCHAR(50) NOT NULL, Role NVARCHAR(20) NOT NULL DEFAULT 'operator' ); CREATE TABLE House ( HouseId INT IDENTITY(1,1) PRIMARY KEY, HouseNo NVARCHAR(20) NOT NULL UNIQUE, Address NVARCHAR(200) NOT NULL, RoomCount INT NOT NULL DEFAULT 1, Area DECIMAL(8,2) NOT NULL, MonthlyRent DECIMAL(10,2) NOT NULL, Status INT NOT NULL DEFAULT 0, Remark NVARCHAR(500) NULL ); CREATE TABLE Tenant ( TenantId INT IDENTITY(1,1) PRIMARY KEY, Name NVARCHAR(50) NOT NULL, Phone NVARCHAR(20) NOT NULL, IdCard NVARCHAR(18) NOT NULL, CreateTime DATETIME NOT NULL DEFAULT GETDATE() ); CREATE TABLE Lease ( LeaseId INT IDENTITY(1,1) PRIMARY KEY, HouseId INT NOT NULL REFERENCES House(HouseId), TenantId INT NOT NULL REFERENCES Tenant(TenantId), StartDate DATE NOT NULL, EndDate DATE NOT NULL, MonthlyRent DECIMAL(10,2) NOT NULL, Deposit DECIMAL(10,2) NOT NULL DEFAULT 0, Status INT NOT NULL DEFAULT 1, CreateTime DATETIME NOT NULL DEFAULT GETDATE() ); CREATE TABLE Payment ( PaymentId INT IDENTITY(1,1) PRIMARY KEY, LeaseId INT NOT NULL REFERENCES Lease(LeaseId), PayDate DATETIME NOT NULL DEFAULT GETDATE(), Amount DECIMAL(10,2) NOT NULL, PayMethod NVARCHAR(20) NOT NULL DEFAULT '现金' );注意租约表的两个外键:HouseId 指向 House,TenantId 指向 Tenant。课程设计评分标准里“表间关系”这一项,凭这两行 REFERENCES 就能拿分。
连接字符串写在 App.config 里:
<connectionStrings> <add name="RentDB" connectionString="Server=127.0.0.1,1433;Database=RentDB;User ID=sa;Password=123456;" providerName="System.Data.SqlClient" /> </connectionStrings>用 IP 加端口而不是Server=.\SQLEXPRESS,是为了换机器演示时不踩实例名的坑。代码侧读取连接串并测试连通:
using System.Configuration; using System.Data.SqlClient; string connStr = ConfigurationManager.ConnectionStrings["RentDB"].ConnectionString; using (SqlConnection conn = new SqlConnection(connStr)) { conn.Open(); // 抛异常说明连接串或网络有问题 }这段代码跑通,等于打通了整套系统的“任督二脉”,后面所有功能都是在这个连接上做文章。
3. C#实战:把登录、房源管理、签约退租做成能演示的功能
数据库课程设计的源码部分,不需要写得多高级,但要把每个操作的完整链路演示清楚:用户在界面上点按钮,数据落到哪张表,界面怎么刷新。下面按一条业务主线走:登录、管房源、签租约、退租、看统计。
3.1 登录与角色判断:参数化查询的正确写法
登录是整套系统唯一一张带条件查询的用户表操作。很多小白写成拼字符串:SELECT * FROM Users WHERE UserName = '+ textBox.Text +'。这样在课程设计验收时当场输入一个单引号就能让程序报错,老师一眼就能看出 SQL 注入问题。用参数化查询:
public string Login(string userName, string password) { string sql = "SELECT Role FROM Users WHERE UserName = @name AND Password = @pwd"; using (SqlConnection conn = new SqlConnection(_connStr)) using (SqlCommand cmd = new SqlCommand(sql, conn)) { cmd.Parameters.Add("@name", SqlDbType.NVarChar, 50).Value = userName; cmd.Parameters.Add("@pwd", SqlDbType.NVarChar, 50).Value = password; conn.Open(); object role = cmd.ExecuteScalar(); return role?.ToString(); } }ExecuteScalar 返回第一行第一列,正好拿角色。参数用cmd.Parameters.Add指定类型和长度,不要图省事全用 AddWithValue——AddWithValue 会按 C# 侧类型推断 SQL 参数类型,遇到 nvarchar 和 varchar 混用的情况,索引可能失效,课程设计里虽然不是性能场景,但养成的习惯是好的。
登录成功后把角色存到全局变量:管理员看到“房屋管理、租约管理、系统设置”等所有菜单,操作员只能看到业务功能。这个控制逻辑写在主窗体的菜单加载事件里,按 Role 判断 Visible 即可。
3.2 房源信息增删改查:DataGridView绑定与状态管理
房源管理是整套系统里最标准的增删改查页面。查询用 SqlDataAdapter 直接填 DataTable,这样绑定 DataGridView 时不需要手动一行行构造:
public DataTable GetAllHouses() { string sql = @"SELECT HouseId, HouseNo, Address, RoomCount, Area, MonthlyRent, CASE Status WHEN 0 THEN N'空置' WHEN 1 THEN N'已租' ELSE N'维修' END AS StatusText FROM House ORDER BY HouseNo"; DataTable table = new DataTable(); using (SqlConnection conn = new SqlConnection(_connStr)) using (SqlDataAdapter adapter = new SqlDataAdapter(sql, conn)) { adapter.Fill(table); return table; } }CASE 直接在 SQL 里把状态数字转成中文,界面侧就不用再写一层翻译逻辑。新增房屋的插入语句:
public bool AddHouse(HouseInfo house) { string sql = @"INSERT INTO House (HouseNo, Address, RoomCount, Area, MonthlyRent, Status, Remark) VALUES (@no, @address, @room, @area, @rent, @status, @remark)"; using (SqlConnection conn = new SqlConnection(_connStr)) using (SqlCommand cmd = new SqlCommand(sql, conn)) { cmd.Parameters.Add("@no", SqlDbType.NVarChar, 20).Value = house.HouseNo; cmd.Parameters.Add("@address", SqlDbType.NVarChar, 200).Value = house.Address; cmd.Parameters.Add("@room", SqlDbType.Int).Value = house.RoomCount; cmd.Parameters.Add("@area", SqlDbType.Decimal).Value = house.Area; cmd.Parameters.Add("@rent", SqlDbType.Decimal).Value = house.MonthlyRent; cmd.Parameters.Add("@status", SqlDbType.Int).Value = 0; // 新增房源默认空置 cmd.Parameters.Add("@remark", SqlDbType.NVarChar, 500).Value = house.Remark ?? ""; conn.Open(); return cmd.ExecuteNonQuery() > 0; } }注意house.Remark ?? "",Nullable 字段不处理会变成空字符串插入失败,这是 C# 里最容易踩的类型边界。执行成功后,重新调用 GetAllHouses 并重新绑定 DataSource,完成一次“增”的闭环。
删除房源时要小心,这就是第 4 章要重点讲的外键坑。现在先记住一个原则:删除不是 UPDATE 一下 IsDeleted 字段就是万能的,关键看你有没有历史租约引用它。
3.3 签约与退租:SqlTransaction保证数据一致
签约这个动作,业务上涉及两张表:往 Lease 表插入一条租约记录,同时把 House 表的 Status 改成 1(已租)。如果不用事务,插入租约成功但更新房屋状态失败,系统里就出现一套“已出租的房屋还能再租给别人”的脏数据。
public bool SignLease(LeaseInfo lease, int houseId, int tenantId) { string insertLease = @"INSERT INTO Lease (HouseId, TenantId, StartDate, EndDate, MonthlyRent, Deposit, Status) VALUES (@houseId, @tenantId, @start, @end, @rent, @deposit, 1)"; string updateHouse = "UPDATE House SET Status = 1 WHERE HouseId = @houseId AND Status = 0"; using (SqlConnection conn = new SqlConnection(_connStr)) { conn.Open(); SqlTransaction tran = conn.BeginTransaction(); try { SqlCommand cmd1 = new SqlCommand(insertLease, conn, tran); cmd1.Parameters.Add("@houseId", SqlDbType.Int).Value = houseId; cmd1.Parameters.Add("@tenantId", SqlDbType.Int).Value = tenantId; cmd1.Parameters.Add("@start", SqlDbType.Date).Value = lease.StartDate; cmd1.Parameters.Add("@end", SqlDbType.Date).Value = lease.EndDate; cmd1.Parameters.Add("@rent", SqlDbType.Decimal).Value = lease.MonthlyRent; cmd1.Parameters.Add("@deposit", SqlDbType.Decimal).Value = lease.Deposit; cmd1.ExecuteNonQuery(); SqlCommand cmd2 = new SqlCommand(updateHouse, conn, tran); cmd2.Parameters.Add("@houseId", SqlDbType.Int).Value = houseId; int affected = cmd2.ExecuteNonQuery(); if (affected == 0) { throw new InvalidOperationException("房屋状态不是空置,无法签约"); } tran.Commit(); return true; } catch { tran.Rollback(); return false; } } }这段代码是整套源码里最容易被老师截图放进报告的。UPDATE 语句里的AND Status = 0是一个乐观锁思想:如果两个人同时签同一套房子,第二个人执行更新时 affected 为 0,直接被拦下。
退租是反向操作,业务逻辑更复杂一些:把 Lease.Status 改成 0,把 House.Status 改成 0,同时还要根据已交租金和实际租期算一笔结算账单写入 Payment。这三步同样放在一个事务里。很多小白退租只改一个状态,导致房子显示空置但租约还挂着,演示到这一步必然露馅。
3.4 房租统计与出租率:用SQL完成课程设计的加分查询
课程设计报告里必须有“统计报表”这一章,老师也默认会问“这系统能统计什么”。不需要做复杂的图表控件,两条 SQL 就能撑起这个模块。
按月汇总房租收入:
SELECT DATEPART(YEAR, PayDate) AS 年份, DATEPART(MONTH, PayDate) AS 月份, SUM(Amount) AS 实收金额 FROM Payment GROUP BY DATEPART(YEAR, PayDate), DATEPART(MONTH, PayDate) ORDER BY 年份, 月份;按当前房源算出租率:
SELECT CAST(SUM(CASE WHEN Status = 1 THEN 1 ELSE 0 END) AS DECIMAL(5,2)) / COUNT(*) AS 出租率 FROM House;把这两个查询结果塞进 DataTable,绑定到 DataGridView,再提供一个“导出 CSV”按钮,报表模块就齐了。导出 CSV 时用 UTF-8 with BOM 编码,Excel 打开中文才不会乱码:
var sb = new StringBuilder(); foreach (DataColumn col in table.Columns) { sb.Append(col.ColumnName).Append(','); } sb.Length--; // 去掉最后一个多余的逗号 sb.AppendLine(); foreach (DataRow row in table.Rows) { foreach (DataColumn col in table.Columns) { sb.Append(row[col.ColumnName]).Append(','); } sb.Length--; sb.AppendLine(); } System.IO.File.WriteAllText("房租统计.csv", sb.ToString(), Encoding.UTF8);这个 CSV 导出方案不需要装 Office 组件,在机房验收时不会因为机器没装 Excel 而翻车。
4. 常见问题与避坑:5个让小白翻车的数据库细节
课程设计翻车很少翻在业务逻辑上,多半是栽在环境、编码和约束这些小细节上。下面这 5 条几乎每年都有人踩,按“现象 → 原因 → 解决”记录,演示前逐条对照检查。
4.1 连接字符串换机器就连不上数据库
现象:在自己笔记本上运行正常,到教室电脑一打开就抛 SqlException,提示“在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误”。
原因:连接字符串写死了本机的 SQL Server 实例名,比如Server=.\SQLEXPRESS,换一台机器实例名不对;或者教室机器的 SQL Server 服务没启动;或者用了 Windows 身份验证而当前登录用户没有数据库权限。
解决:统一改成 SQL Server 身份验证,写成Server=127.0.0.1,1433;Database=RentDB;User ID=sa;Password=123456;,并放到 App.config。演示前一天到教室先跑一次conn.Open()测试代码,别等老师坐在旁边了才发现连不上。提交源码时把 App.config 一起交,并在报告里写清楚“数据库连接串在配置文件中修改”。
4.2 中文存进去变成问号
现象:窗体里正常输入“张伟”,保存到数据库后变成“张??”或者“???”,界面一刷新显示乱码。
原因:列类型用了 varchar。SQL Server 的 varchar 按非 Unicode 存储,中文字符在数据库代码页不支持时直接丢失。MySQL 场景下则是连接字符串没指定 utf8mb4。
解决:SQL Server 建表一律用 nvarchar,插入时参数类型用 SqlDbType.NVarChar。如果你的建表脚本里已经有INSERT INTO … VALUES (N'张伟')这种带 N 前缀的写法,说明你已经在往正确的方向靠了。MySQL 用户记得在连接字符串里加CharSet=utf8mb4;,同时表字符集也要对齐。检查方法很简单:在 SSMS 里直接查询列的类型,看到 varchar 就改。
4.3 删除数据提示外键约束冲突
现象:删一条房屋记录,抛“DELETE 语句与 REFERENCE 约束冲突”,看半天不知道哪里被引用了。
原因:Lease 表里有外键指向 House,这恰恰说明你的外键建对了。数据库不允许删有子表引用的父表,这是约束在起作用。
解决:用软删除。给 House 表加一个 IsDeleted 字段(0 正常 / 1 已删除),删除操作改为 UPDATE:
UPDATE House SET IsDeleted = 1 WHERE HouseId = @houseId;所有列表查询默认加WHERE IsDeleted = 0。这样历史租约的 HouseId 还能继续关联,报告里也能写“软删除保留了历史数据,便于事后审计”,比硬删高级一个档次。如果实在需要物理删除,就按“先删子表、再删父表”的顺序处理,但租约关联的缴费记录会一起丢,不推荐。
4.4 DataGridView列标题变成英文字段名
现象:给 DataGridView 设了 DataSource,列头显示 HouseId、HouseNo、Area 这种英文,列顺序也跟你想的不一样。
原因:DataGridView 的 AutoGenerateColumns 默认为 true,它会自动为数据源里的每一列生成一列,字段名直接当表头。想自定义列标题,就得关掉它。
解决:在设计器里把 AutoGenerateColumns 设为 false,然后手动配置列:
dataGridView1.AutoGenerateColumns = false; dataGridView1.Columns.Clear(); dataGridView1.Columns.Add(new DataGridViewTextBoxColumn { DataPropertyName = "HouseNo", HeaderText = "房号", Width = 100 }); dataGridView1.Columns.Add(new DataGridViewTextBoxColumn { DataPropertyName = "Address", HeaderText = "地址", Width = 200 });DataPropertyName 对应 SQL 查询返回的字段名,HeaderText 是界面显示的标题。推荐把 SQL 里的字段名改成无下划线的驼峰写法,免得绑定时字段名对不上。
4.5 按日期范围查询漏数据
现象:选“2024-01-01 到 2024-01-31”,查出来的账单少了 1 月 31 日晚上交的那几条。退租截止日期也总是差一天。
原因:DateTime 比较的边界问题。很多人拼 SQL 时把 C# 的 DateTime 转成"2024-01-31"字符串,数据库把它解析成 2024-01-31 00:00:00,那么当天 23:59 交的费就被排除在外。用 BETWEEN 也一样,BETWEEN 是闭区间,但字符串转出来的起点是零点。
解决:传参数不要转字符串,直接传 DateTime 对象;查某一天用左闭右开区间:
DateTime start = dtpBegin.Value.Date; // 当天 0 点 DateTime end = start.AddDays(1); // 次日 0 点 cmd.Parameters.Add("@start", SqlDbType.DateTime).Value = start; cmd.Parameters.Add("@end", SqlDbType.DateTime).Value = end;SQL 侧写成PayDate >= @start AND PayDate < @end。退租截止也是同理,StartDate 存当天零点,EndDate 逻辑上就是下一周期起点,不要让客户“多住一晚”。
5. 验收与答辩:演示路径、报告素材和必答问题
源码写完只是第一步,能不能被评上优秀课程设计,还要看演示和报告怎么组织。我最推荐的演示路径是一条完整业务链:登录 → 添加房源 → 新增租客 → 签约 → 缴费 → 退租 → 查看统计报表。每个步骤说清楚“刚才那个操作改了哪张表哪个字段”,比挨个窗体演示更有说服力。
演示数据提前准备 5 套左右,用真实感强的地址和名字,比如“阳光花园 3 栋 502”“月租金 1800”。空数据库现场录入既慢又容易手误,老师看着也乏味。签约环节务必现场操作一次,让老师看到 Lease 表多了一条记录、House 表状态从“空置”变“已租”,这比任何 PPT 截图都直观。
报告素材方面,ER 图用 draw.io 画清楚三张核心表和它们的关系,数据字典列出字段类型和说明,核心代码只贴登录参数化查询、事务签约、统计 SQL 这三段。整份源码包按“sln + SQL脚本 + 数据库备份 + 报告文档”四部分整理,老师拿到手能直接跑,这个习惯本身就会加分。
答辩被问频率最高的问题就几个:为什么用事务、为什么冗余状态字段、外键有什么用、连接为什么用 using。提前把 2.1 和 3.3 里的思路用自己的话复述一遍,基本都能接住。
我当年做这套项目的时候,偷懒先写了界面再补表结构,结果中途改了三次数据库脚本,界面跟着返工,浪费了整整一个周末。后来养成的习惯是:任何和数据库打交道的课程设计,先画 ER 图、定字段类型、列外键关系,再碰界面代码。这套顺序看着慢,实际是最快的路,希望帮到你。
本文还有配套的精品资源,点击获取