
简介这是一份面向高校计算机专业学生及C#初学者的数据库课程设计实战资源聚焦房屋租赁业务场景完整呈现从需求分析、数据库建模到C#桌面应用开发的全流程实践。资源包含可直接运行的VS解决方案.sln、核心业务逻辑源码25个.cs文件、界面资源.resx与.png、数据库脚本roomrentsystem.sql、两份Word格式课程设计报告含封面、ER图、数据流图等关键图表以及MySQL连接驱动安装包覆盖开发、部署与文档全环节。压缩包共71个文件总大小12.86MB结构清晰模块划分明确便于学习者理解MVC雏形、ADO.NET数据访问及WinForm界面交互逻辑。目前已有146人下载学习特别适合课程设计选题参考、期末项目复现或数据库前端联动开发入门训练。1. C#房屋租赁管理系统不是写个登录框就叫课程设计它得能扛住真实房东的催租电话你交上去的“课程设计”真能在毕业答辩现场被老师点开、查房、改价、退租、导出Excel报表还不出错吗很多同学用C#拖几个TextBoxButton连数据库都用Access随便建三张表一跑就报“未将对象引用到对象实例”——这不是代码问题是没想清楚一个真实的房屋租赁场景里房东要同时管5套房子、3个租客、2份合同到期提醒、1个押金流水系统得在不崩溃的前提下把“张三租了朝阳区那套两居室月租6800押一付三下月15号该续签”这件事从录入、查询、修改到导出全链路闭环跑通。这不是炫技是数据库设计业务逻辑界面交互的三重校验。本篇不讲抽象理论只拆解我带过6届学生做这个题目的血泪经验怎么用C# WinForms SQL Server或SQLite搭出一套能演示、能调试、能改、能讲清设计思路的最小可行系统。重点不在“多酷”而在“哪一步卡住你就知道去哪查日志”。适合刚学完ADO.NET、正对着《数据库原理》课本发懵但又必须两周内交源码报告的大三同学。2. 用C# WinForms SQL Server搭建核心骨架从建库到主窗体5步跑通增删改查闭环课程设计最怕“开头五分钟后面两星期调连接字符串”。我们跳过所有花哨框架用最稳的WinFormsSQL Server组合确保你本地装好SQL Server Express或SQLite就能立刻动手。关键不是工具多新而是每一步都能看到数据落地。2.1 数据库设计三张表撑起全部业务字段命名直击痛点别一上来就建10张表。真实租赁业务的核心实体就三个房源House、租客Tenant、合同Contract。我让学生反复画ER图最后收敛到这三张表字段名全部用业务语言拒绝tbl_user_info这种玄学命名表名字段类型说明是否为空备注HouseHouseIdINT PK IDENTITY房源唯一IDNOT NULL主键自增AddressNVARCHAR(200)详细地址含小区名、楼层、门牌NOT NULL搜索和打印报表直接用AreaDECIMAL(6,2)建筑面积㎡NOT NULL用于租金单价计算RentPriceDECIMAL(10,2)月租金元NOT NULL合同生成时锁定StatusTINYINT状态0空置/1已租/2维修中NOT NULL避免重复出租TenantTenantIdINT PK IDENTITY租客IDNOT NULLNameNVARCHAR(50)姓名NOT NULLPhoneNVARCHAR(20)手机号带格式验证NOT NULL后续短信通知基础IdCardNVARCHAR(18)身份证号唯一索引NOT NULL防止同一人重复签约ContractContractIdINT PK IDENTITY合同IDNOT NULLHouseIdINT FK关联房源NOT NULL外键约束TenantIdINT FK关联租客NOT NULL外键约束StartDateDATE起租日期NOT NULL用于计算到期日EndDateDATE到期日期NOT NULLStartDate 12个月自动计算DepositDECIMAL(10,2)押金金额NOT NULL单独字段不混入租金StatusTINYINT合同状态0生效/1已终止/2已续签NOT NULL控制操作权限提示Status字段统一用TINYINT而非字符串避免拼写错误外键必须加ON DELETE CASCADE删除租客时自动清理其合同否则答辩时老师删个测试数据就崩给你看。2.2 C#连接字符串与基础DAO层手写ConnectionCommand不碰Entity Framework课程设计用EF容易翻车——生成的代码太多出错时根本找不到是Model错了还是Migration错了。我们回归ADO.NET本质手写一个极简DbHelper类只做三件事开连接、执行SQL、关连接。所有SQL语句明文写死方便你debug时复制到SSMS里直接运行// DbHelper.cs public static class DbHelper { private static readonly string ConnectionString Data Source.;Initial CatalogRentalDB;Integrated Securitytrue;; public static int ExecuteNonQuery(string sql, params SqlParameter[] parameters) { using (var conn new SqlConnection(ConnectionString)) { conn.Open(); using (var cmd new SqlCommand(sql, conn)) { cmd.Parameters.AddRange(parameters); return cmd.ExecuteNonQuery(); // 返回影响行数用于判断是否成功 } } } public static DataTable ExecuteQuery(string sql, params SqlParameter[] parameters) { var dt new DataTable(); using (var conn new SqlConnection(ConnectionString)) { conn.Open(); using (var cmd new SqlCommand(sql, conn)) { cmd.Parameters.AddRange(parameters); using (var adapter new SqlDataAdapter(cmd)) { adapter.Fill(dt); // 直接填充DataTableWinForms绑定GridView最省事 } } } return dt; } }参数说明ConnectionString里Initial CatalogRentalDB必须和你创建的数据库名完全一致大小写敏感ExecuteNonQuery返回值必须检查if (rowsAffected 0) MessageBox.Show(未找到对应房源);——这是答辩时老师最爱问的“没数据时你怎么提示用户”ExecuteQuery返回DataTable而非ListT因为WinForms的DataGridView原生绑定它不用写BindingSource中间层新手零学习成本。2.3 主窗体与导航逻辑用TabControl替代MDI避免子窗体堆叠失控别用MDI多文档界面WinForms的MDI在高DPI屏幕下缩放错乱且子窗体关闭后资源不释放跑两小时内存暴涨。我们用TabControl做主导航每个TabPage对应一个业务模块// MainForm.cs 构造函数中初始化Tab private void InitializeTabs() { tabControl1.TabPages.Clear(); // Tab1房源管理 var housePage new TabPage(房源管理); housePage.Controls.Add(new HouseManagementUserControl()); // 自定义用户控件 tabControl1.TabPages.Add(housePage); // Tab2租客管理 var tenantPage new TabPage(租客管理); tenantPage.Controls.Add(new TenantManagementUserControl()); tabControl1.TabPages.Add(tenantPage); // Tab3合同管理核心 var contractPage new TabPage(合同管理); contractPage.Controls.Add(new ContractManagementUserControl()); tabControl1.TabPages.Add(contractPage); }为什么用UserControl不用FormUserControl可复用HouseManagementUserControl里封装了“新增房源”按钮、DataGridView、搜索框下次做“维修记录”模块直接复制粘贴生命周期可控Tab切换时VisibleChanged事件能触发数据刷新避免缓存脏数据答辩演示流畅老师点哪个Tab就加载哪个模块不卡顿。3. 合同生命周期管理从签约、续租到退租用事务保证数据一致性课程设计最容易被挑刺的地方就是“签约时房源状态没改”“退租时押金没退”“续租生成两份合同”。这些不是功能缺陷是业务规则没落地为数据库约束代码事务。我们聚焦Contract表用最朴素的SQL事务解决。3.1 签约一次操作三处写入必须原子性用户点击“签订合同”按钮时要同时完成插入一条Contract记录更新对应House.Status 1已租记录一条DepositLog押金流水虽未在基础表中但实际项目必须有。用单个SQL事务包裹避免中途失败导致房源“假出租”// ContractManagementUserControl.cs private void btnSignContract_Click(object sender, EventArgs e) { try { using (var conn new SqlConnection(DbHelper.ConnectionString)) { conn.Open(); using (var trans conn.BeginTransaction()) { try { // 步骤1插入合同 var insertSql INSERT INTO Contract (HouseId, TenantId, StartDate, EndDate, Deposit, Status) VALUES (houseId, tenantId, startDate, endDate, deposit, 0); DbHelper.ExecuteNonQuery(insertSql, new SqlParameter(houseId, selectedHouseId), new SqlParameter(tenantId, selectedTenantId), new SqlParameter(startDate, dtpStartDate.Value), new SqlParameter(endDate, dtpEndDate.Value), new SqlParameter(deposit, nudDeposit.Value), trans); // 步骤2更新房源状态 var updateHouseSql UPDATE House SET Status 1 WHERE HouseId houseId; DbHelper.ExecuteNonQuery(updateHouseSql, new SqlParameter(houseId, selectedHouseId), trans); // 步骤3记录押金假设已有DepositLog表 var logSql INSERT INTO DepositLog (ContractId, Amount, Type, CreateTime) VALUES (contractId, amount, 1, GETDATE()); // 注意此处需先查出刚插入的ContractId用SCOPE_IDENTITY() var getIdSql SELECT SCOPE_IDENTITY(); var contractId (int)(decimal)DbHelper.ExecuteScalar(getIdSql, trans); DbHelper.ExecuteNonQuery(logSql, new SqlParameter(contractId, contractId), new SqlParameter(amount, nudDeposit.Value), trans); trans.Commit(); // 全部成功才提交 MessageBox.Show(签约成功); } catch { trans.Rollback(); // 任一失败全部回滚 throw; } } } } catch (Exception ex) { MessageBox.Show($签约失败{ex.Message}); } }关键细节SCOPE_IDENTITY()获取刚插入的ContractId比IDENTITY更安全不受触发器影响trans参数传给所有ExecuteNonQuery确保都在同一事务中try/catch嵌套两层外层捕获连接异常内层捕获业务逻辑异常并主动Rollback。3.2 续租不是新建合同而是更新原合同生成新记录很多同学续租直接INSERT新合同导致历史合同丢失。正确做法是将原合同Status设为2已续签插入新合同StartDate 原EndDate 1天EndDate StartDate 12个月更新房源Status保持为1已租// 续租逻辑片段 var updateOldSql UPDATE Contract SET Status 2 WHERE ContractId oldId; DbHelper.ExecuteNonQuery(updateOldSql, new SqlParameter(oldId, oldContractId), trans); var insertNewSql INSERT INTO Contract (HouseId, TenantId, StartDate, EndDate, Deposit, Status) VALUES (houseId, tenantId, startDate, endDate, deposit, 0); // 计算新起租日DateTime oldEnd ...; DateTime newStart oldEnd.AddDays(1); DbHelper.ExecuteNonQuery(insertNewSql, new SqlParameter(houseId, houseId), new SqlParameter(tenantId, tenantId), new SqlParameter(startDate, newStart), new SqlParameter(endDate, newStart.AddMonths(12)), new SqlParameter(deposit, deposit), trans);为什么续租要保留旧合同法律效力原合同条款如违约金仍有效审计需求老师问“去年租金是多少”你能查历史合同报表统计按年统计签约量不能只算最新合同。3.3 退租四步操作缺一不可押金退还必须留痕退租不是删合同必须更新合同Status 1已终止更新房源Status 0空置插入押金退还记录DepositLog.Type 2可选生成退租确认单PDF用iTextSharp课程设计可简化为打印预览。// 退租核心SQL var updateContractSql UPDATE Contract SET Status 1 WHERE ContractId id; var updateHouseSql UPDATE House SET Status 0 WHERE HouseId houseId; var refundLogSql INSERT INTO DepositLog (ContractId, Amount, Type, CreateTime) VALUES (id, amount, 2, GETDATE()); // 执行顺序先更新合同和房源再记日志避免日志写了但状态没改 DbHelper.ExecuteNonQuery(updateContractSql, new SqlParameter(id, contractId), trans); DbHelper.ExecuteNonQuery(updateHouseSql, new SqlParameter(houseId, houseId), trans); DbHelper.ExecuteNonQuery(refundLogSql, new SqlParameter(id, contractId), new SqlParameter(amount, actualRefund), trans);注意actualRefund可能小于Deposit扣维修费所以退租界面必须有“应退金额”和“实退金额”两个输入框不能直接填押金字段。4. 避坑课程设计答辩高频翻车点3条血泪经验帮你绕开别等答辩当天被老师一句“你这删除功能没加确认框吧”当场宕机。以下是我在机房盯了6届学生调试时总结出的最高频、最隐蔽、最易被忽略的3个坑每条都附现象、原因、解法4.1 现象DataGridView双击编辑后点保存按钮没反应F5刷新数据还在原因WinForms的DataGridView默认EditMode EditOnEnter但如果你没设置DataSource为BindingSource或者BindingSource.DataSource指向的是DataTable而非BindingListT那么单元格修改只是UI层变化不会自动同步到后端数据库。解决在DataGridView属性窗口将EditMode改为EditOnKeystrokeOrF2更符合用户直觉保存按钮代码里必须显式调用DataTable.GetChanges()判断是否有修改而不是直接执行UPDATEvar changes dt.GetChanges(); // dt是绑定的DataTable if (changes ! null) { foreach (DataRow row in changes.Rows) { if (row.RowState DataRowState.Modified) { // 构造UPDATE语句用原值WHERE新值SET string sql UPDATE House SET Addressaddr WHERE HouseIdid AND AddressoldAddr; DbHelper.ExecuteNonQuery(sql, new SqlParameter(addr, row[Address]), new SqlParameter(id, row[HouseId]), new SqlParameter(oldAddr, row[Address, DataRowVersion.Original])); // 关键用原始值做WHERE } } }4.2 现象添加租客时身份证号重复程序直接抛出SqlException界面崩掉原因数据库Tenant.IdCard字段加了UNIQUE约束但C#层没做前置校验异常直接向上抛到UI线程。解决永远不要依赖数据库报错做业务校验。在btnAddTenant_Click里先查重string checkSql SELECT COUNT(*) FROM Tenant WHERE IdCard idCard; int count (int)DbHelper.ExecuteScalar(checkSql, new SqlParameter(idCard, txtIdCard.Text)); if (count 0) { MessageBox.Show(该身份证号已存在请核对); return; // 阻断后续插入 } // 只有查重通过才执行INSERT同时在SQL Server中为IdCard字段加CHECK约束CHECK (LEN(IdCard) 18)双重保险。4.3 现象导出Excel报表时中文全是乱码数字列宽度挤成一团原因用Microsoft.Office.Interop.Excel需要安装Office且不同版本兼容性差用NPOI又容易漏掉Workbook.CreateSheet()后的编码设置。解决课程设计用最稳方案——导出CSV用记事本打开自动识别UTF-8private void ExportToCsv(DataTable dt, string fileName) { var sb new StringBuilder(); // 写表头 foreach (DataColumn col in dt.Columns) { sb.Append($\{col.ColumnName}\,); } sb.AppendLine(); // 写数据行 foreach (DataRow row in dt.Rows) { foreach (var item in row.ItemArray) { sb.Append($\{item.ToString().Replace(\, \\)}\,); // 转义双引号 } sb.AppendLine(); } File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8); // 关键指定UTF8编码 }为什么CSV比Excel稳妥不依赖Office环境老师电脑没装Excel也能打开Encoding.UTF8确保中文不乱码Replace(\, \\)处理字段内含双引号如地址“北京市朝阳区国贸大厦”答辩时老师说“导出看看”你3秒生成文件比等Excel COM组件初始化快10倍。5. 报告撰写与答辩话术让老师觉得“这学生真懂数据库设计”课程设计报告不是代码说明书而是设计决策的证据链。老师扫一眼就知道你有没有思考。我要求学生报告里必须包含这三页硬核内容缺一不可5.1 数据库设计页ER图字段说明表拒绝截图SSMS别贴一张模糊的SSMS表结构截图用Draw.io或Visio画标准ER图重点标出实体间关系类型1对多必须标注基数外键连线旁注明约束动作如House→Contract: ON DELETE CASCADEStatus字段的取值含义用文字框注明0空置/1已租…而不是让老师猜。字段说明表必须和代码里DbHelper的SQL参数一一对应字段名类型业务含义校验规则示例值House.StatusTINYINT房源当前状态值域{0,1,2}1表示已出租Contract.EndDateDATE合同截止日必须≥StartDate30天2025-06-15提示答辩时老师问“为什么Status用TINYINT不用VARCHAR”答“避免拼写错误导致WHERE条件失效且TINYINT存储空间小、索引效率高符合第三范式‘属性不可再分’”。5.2 关键代码页只贴3段每段带“为什么这么写”的注释报告里代码不超过3段且必须是体现设计深度的片段事务处理代码上文签约那段——证明你理解ACIDDataGridView数据同步代码4.1节——证明你懂UI与数据层分离身份证查重约束双重校验代码4.2节——证明你懂防御性编程。每段代码下方用一句话解释设计意图“此处使用SCOPE_IDENTITY()而非IDENTITY因IDENTITY在触发器存在时会返回触发器内生成的ID导致押金日志关联错误合同。”5.3 测试用例页用表格列5个真实场景附截图结果别写“测试通过”。用表格展示你真跑过场景操作步骤预期结果实际截图贴小图是否通过房源空置状态校验新增房源→立即签约→再点“签约”按钮提示“该房源已出租”✓合同续租数据连续性查看2024年合同→点击续租→查看新合同起租日新合同StartDate 原EndDate 1天✓押金退还留痕退租→填写实退金额→导出押金流水表流水表含Type2记录Amount实退金额✓为什么这页最关键证明你不是“代码写完就交”而是真测过边界情况截图用Windows自带截图工具标红关键区域如提示框文字、日期字段显得专业“是否通过”栏打✓不通过的写明原因如“未实现维修扣款功能待优化”反而显诚实。最后说句实在的我见过太多学生花一周调通登录界面却用三天突击写报告答辩时被问“你这个外键删了会怎样”直接卡壳。真正的课程设计能力不在于你写了多少行代码而在于你能否用数据库思维解释每一行代码存在的理由。这套方案跑通后你不仅能交差更能把“数据库设计”“事务控制”“UI数据绑定”这几个词真正变成自己技术简历里的肌肉记忆。希望帮到你。本文还有配套的精品资源点击获取