简介:这份资源是面向高校计算机相关专业学生的数据库课程设计参考项目,主题为超市会员管理系统,采用C#语言开发,服务端基于ASP.NET Core,客户端使用Blazor配合Ant Design Blazor组件库实现,适合正在准备课程设计或希望学习前后端分离架构的开发者参考。压缩包共869个文件,约40.96MB,其中包含74个cs源码文件、10个razor组件、9个css样式、3个csproj项目文件及1个sln解决方案文件,另有258个dll依赖库、398个gz资源文件以及json配置、pdb调试符号等,完整覆盖服务端、客户端与共享层三个项目模块。目前已有2377人学习下载,说明该案例在课程设计场景中具有较高的参考价值。通过这份源码,读者可以了解会员信息管理、积分与消费记录等典型业务模块的代码组织方式,掌握Blazor组件化开发与ASP.NET Core接口设计的配合思路,并借助现成的项目结构快速搭建自己的课程设计框架,减少从零摸索的时间成本。
1. 超市会员管理系统:从课程设计到能跑通的 C# 数据库项目
很多同学拿到“C#语言数据库课程设计超市会员管理系统源代码.zip”这类资源时,第一反应是解压、双击 .sln、按 F5,然后发现数据库连不上、报表打不开、登录报错。这个标题背后其实是一套典型的桌面端 CRUD 项目:C# WinForms 做界面,SQL Server 或 Access 做数据存储,核心业务是会员信息、积分、消费记录和简单的统计查询。它适合正在做数据库课程设计的学生,也适合想用一个小项目把 ADO.NET、SQL 语句和三层结构串起来的初学者。真正要解决的问题不是“有没有源码”,而是“怎么让源码在自己机器上跑起来,并且能讲清楚每一层在干什么”。下面按实际落地顺序拆开讲。
2. 先看清项目骨架:WinForms + ADO.NET + SQL Server 的典型分层
2.1 为什么这类课程设计偏爱 WinForms 和 ADO.NET
超市会员管理系统的业务边界很清晰:会员注册、充值、消费、积分累计、查询和简单报表。用 WinForms 做界面,拖控件就能出原型,老师验收时看得见摸得着;用 ADO.NET 直接写 SQL,代码量可控,不会像 Entity Framework 那样隐藏太多细节,答辩时反而不好解释“数据到底怎么进的库”。常见做法是分三层:UI 层放窗体,BLL 层放业务规则,DAL 层放 SqlConnection、SqlCommand 和 DataTable。这样分层的好处是,哪怕你只改数据库连接字符串,也不用翻遍所有窗体代码。
从热词里能看到“数据库增删改查”“c#与access”“mysql数据库修改结构”这些词,说明很多人卡在数据库选型和连接方式上。我的建议是:如果学校机房装的是 SQL Server,就用 SQL Server;如果只让用 Access,就把 DAL 层的 OleDb 连接换掉,业务逻辑基本不动。不要一开始就纠结 MySQL,除非题目明确要求。
2.2 用 SQL 脚本建库:三张核心表就能撑起业务
拿到源码后,先别急着打开 Visual Studio。第一步应该是找 .sql 文件或 .mdf 文件。如果没有,就按下面这个最小结构自己建。会员表存基本信息和积分,消费记录表存每次交易,管理员表存登录账号。
-- 创建数据库 CREATE DATABASE SupermarketMemberDB; GO USE SupermarketMemberDB; GO -- 会员表:卡号为主键,积分默认0 CREATE TABLE Members ( MemberId VARCHAR(20) PRIMARY KEY, MemberName NVARCHAR(50) NOT NULL, Phone VARCHAR(20), Points INT DEFAULT 0, Balance DECIMAL(10,2) DEFAULT 0, RegisterDate DATETIME DEFAULT GETDATE() ); -- 消费记录表:外键关联会员 CREATE TABLE ConsumeRecords ( RecordId INT IDENTITY(1,1) PRIMARY KEY, MemberId VARCHAR(20) FOREIGN KEY REFERENCES Members(MemberId), ConsumeAmount DECIMAL(10,2) NOT NULL, ConsumeDate DATETIME DEFAULT GETDATE(), Remark NVARCHAR(100) ); -- 管理员表:用于登录 CREATE TABLE Admins ( AdminId VARCHAR(20) PRIMARY KEY, Password VARCHAR(50) NOT NULL ); INSERT INTO Admins VALUES ('admin', '123456');这段脚本的逻辑很直接:Members 表用 MemberId 做业务主键,方便刷卡或输入卡号;ConsumeRecords 用自增 RecordId 做物理主键,避免同一会员同一时间多笔消费冲突;Admins 表只做最简单的登录校验。参数上注意 DECIMAL(10,2) 表示最多 10 位数、2 位小数,金额够用;Points 用 INT,积分一般不会溢出。执行完脚本后,在 SSMS 里刷新一下,确认三张表都在。
2.3 改连接字符串:最容易翻车的一步
源码里通常会在 App.config 或 DbHelper 类里写死连接字符串。你需要在解决方案里搜索“Data Source”或“connectionString”。常见的坑是:源码作者用的是自己的机器名或 SQL Express 实例名,你直接运行就会报“找不到服务器”。改成自己环境的写法:
<!-- App.config 中的连接字符串,Data Source 用本机默认实例 --> <connectionStrings> <add name="SupermarketDB" connectionString="Data Source=.;Initial Catalog=SupermarketMemberDB;Integrated Security=True" providerName="System.Data.SqlClient"/> </connectionStrings>Data Source=.表示本机默认实例;如果装的是 SQL Express,要写成Data Source=.\SQLEXPRESS。Integrated Security=True表示用 Windows 身份验证,不用输账号密码。如果你用的是 SQL 账号登录,就改成User Id=sa;Password=你的密码。改完后,在代码里用ConfigurationManager.ConnectionStrings["SupermarketDB"].ConnectionString读取,不要硬编码在每个窗体里。
3. 把增删改查跑通:从登录到会员充值的最小闭环
3.1 登录窗体验证:别把密码明文写在代码里
很多课程设计源码的登录逻辑是if (txtUser.Text == "admin" && txtPwd.Text == "123456"),这种写法答辩时容易被问住。改成从 Admins 表查:
// 登录按钮事件:参数化查询防止 SQL 注入 private void btnLogin_Click(object sender, EventArgs e) { string sql = "SELECT COUNT(*) FROM Admins WHERE AdminId=@id AND Password=@pwd"; using (SqlConnection conn = new SqlConnection(connStr)) { SqlCommand cmd = new SqlCommand(sql, conn); cmd.Parameters.AddWithValue("@id", txtUser.Text.Trim()); cmd.Parameters.AddWithValue("@pwd", txtPwd.Text.Trim()); conn.Open(); int count = (int)cmd.ExecuteScalar(); if (count > 0) { new MainForm().Show(); this.Hide(); } else { MessageBox.Show("账号或密码错误"); } } }这里用AddWithValue传参,而不是拼接字符串,是为了防止' OR '1'='1这种注入。ExecuteScalar返回第一行第一列,适合做 COUNT 查询。注意using会自动释放连接,不用手动 Close。如果你用的是 Access,把 SqlConnection 换成 OleDbConnection,参数占位符改成?,顺序要对应。
3.2 会员充值:事务保证余额和记录同时成功
充值业务要同时更新 Members 表的 Balance 和插入 ConsumeRecords 表。如果只成功一半,账就乱了。用事务包起来:
// 充值按钮:事务确保两条 SQL 要么都成功,要么都回滚 private void btnRecharge_Click(object sender, EventArgs e) { decimal amount = decimal.Parse(txtAmount.Text); using (SqlConnection conn = new SqlConnection(connStr)) { conn.Open(); SqlTransaction tran = conn.BeginTransaction(); try { string sql1 = "UPDATE Members SET Balance=Balance+@amt WHERE MemberId=@id"; SqlCommand cmd1 = new SqlCommand(sql1, conn, tran); cmd1.Parameters.AddWithValue("@amt", amount); cmd1.Parameters.AddWithValue("@id", txtMemberId.Text.Trim()); cmd1.ExecuteNonQuery(); string sql2 = "INSERT INTO ConsumeRecords(MemberId,ConsumeAmount,Remark) VALUES(@id,@amt,'充值')"; SqlCommand cmd2 = new SqlCommand(sql2, conn, tran); cmd2.Parameters.AddWithValue("@id", txtMemberId.Text.Trim()); cmd2.Parameters.AddWithValue("@amt", amount); cmd2.ExecuteNonQuery(); tran.Commit(); MessageBox.Show("充值成功"); } catch (Exception ex) { tran.Rollback(); MessageBox.Show("充值失败:" + ex.Message); } } }参数说明:BeginTransaction开启事务,两个 SqlCommand 都绑定同一个 tran;Commit提交,Rollback回滚。金额用 decimal 而不是 double,避免浮点误差。如果充值金额允许负数,要在业务层加判断,别让用户输入 -100。
3.3 积分累计规则:消费 1 元积 1 分,但别在 SQL 里写死
积分规则经常变,有的超市是 1 元 1 分,有的是 10 元 1 分。建议把比例放在配置文件或常量里:
// 消费时按比例累计积分,比例可配置 private const int PointsPerYuan = 1; // 1元=1分 private void AddPoints(string memberId, decimal amount) { int points = (int)(amount * PointsPerYuan); string sql = "UPDATE Members SET Points=Points+@p WHERE MemberId=@id"; using (SqlConnection conn = new SqlConnection(connStr)) { SqlCommand cmd = new SqlCommand(sql, conn); cmd.Parameters.AddWithValue("@p", points); cmd.Parameters.AddWithValue("@id", memberId); conn.Open(); cmd.ExecuteNonQuery(); } }这样改比例时只动一个常量。注意(int)会截断小数,如果消费 9.9 元,按 1 元 1 分就是 9 分,不是 10 分。要四舍五入就用Math.Round。
4. 避坑与排查:课程设计里最容易翻车的 5 个地方
4.1 现象:程序报“未将对象引用设置到对象的实例”
原因:窗体加载时 DataGridView 还没绑定数据,或者从数据库读出来是 DBNull,直接 ToString 就炸。解决:在赋值前加if (row["Phone"] != DBNull.Value),或者用Convert.ToString(row["Phone"])。另外检查 InitializeComponent 是否被误删。
4.2 现象:数据库连接测试通过,但程序一运行就提示登录失败
原因:源码里用的是 SQL Server 身份验证,但你机器上只开了 Windows 验证。解决:在 SSMS 里把服务器属性改成“SQL Server 和 Windows 身份验证模式”,重启服务;或者把连接字符串改成Integrated Security=True。如果用的是 Access,检查 .accdb 文件是否被复制到 bin\Debug 目录下。
4.3 现象:DataGridView 显示中文乱码
原因:数据库字段用了 VARCHAR 而不是 NVARCHAR,或者 C# 里用了错误的编码。解决:建表时中文列一律用 NVARCHAR;连接字符串里不用加编码;如果是从 CSV 导入,用Encoding.GetEncoding("GB2312")读。
4.4 现象:充值后余额变了,但消费记录没增加
原因:没有用事务,或者第二条 SQL 执行时报错被 catch 吞掉了。解决:按 3.2 的写法加事务,并且在 catch 里把 ex.Message 弹出来,不要只写“操作失败”。另外检查 ConsumeRecords 的外键是否限制了 MemberId 必须存在。
4.5 现象:换一台电脑就运行不了,提示缺少 .dll
原因:源码引用了第三方控件或本地路径的 DLL。解决:在解决方案里把引用路径改成相对路径,或者把 DLL 复制到项目下的 lib 文件夹,重新添加引用。如果用了 SQLite,还要确认 System.Data.SQLite.dll 的版本和平台(x86/x64)匹配。
5. 进阶技巧:用参数化查询和存储过程把课程设计做出工程味
5.1 把常用查询封装成 DbHelper 类
与其在每个窗体里写new SqlConnection,不如抽一个 DbHelper:
public static class DbHelper { private static string connStr = ConfigurationManager.ConnectionStrings["SupermarketDB"].ConnectionString; // 执行增删改,返回受影响行数 public static int ExecuteNonQuery(string sql, params SqlParameter[] paras) { using (SqlConnection conn = new SqlConnection(connStr)) { SqlCommand cmd = new SqlCommand(sql, conn); cmd.Parameters.AddRange(paras); conn.Open(); return cmd.ExecuteNonQuery(); } } // 执行查询,返回 DataTable public static DataTable ExecuteQuery(string sql, params SqlParameter[] paras) { using (SqlConnection conn = new SqlConnection(connStr)) { SqlDataAdapter adapter = new SqlDataAdapter(sql, conn); adapter.SelectCommand.Parameters.AddRange(paras); DataTable dt = new DataTable(); adapter.Fill(dt); return dt; } } }这样调用时只需DbHelper.ExecuteQuery("SELECT * FROM Members WHERE MemberId=@id", new SqlParameter("@id", id))。参数说明:params关键字允许传任意个参数;SqlDataAdapter自动管理连接打开和关闭,适合查询。
5.2 用存储过程做月度消费统计
如果答辩时想展示“数据库高级一点”,可以写一个存储过程统计某会员某月消费总额:
CREATE PROCEDURE GetMonthlyConsume @MemberId VARCHAR(20), @Month INT, @Year INT AS BEGIN SELECT ISNULL(SUM(ConsumeAmount),0) AS Total FROM ConsumeRecords WHERE MemberId=@MemberId AND MONTH(ConsumeDate)=@Month AND YEAR(ConsumeDate)=@Year; ENDC# 调用时把 CommandType 设为 StoredProcedure:
SqlCommand cmd = new SqlCommand("GetMonthlyConsume", conn); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.AddWithValue("@MemberId", memberId); cmd.Parameters.AddWithValue("@Month", month); cmd.Parameters.AddWithValue("@Year", year);注意ISNULL保证没有记录时返回 0 而不是 null。存储过程的好处是逻辑在数据库端,换前端不用重写。
5.3 验证方法:用 SQL 语句反向核对程序结果
程序跑完后,别只看界面。打开 SSMS,执行SELECT * FROM Members和SELECT * FROM ConsumeRecords,核对余额和积分是否对得上。比如会员 A 充值 100 元,消费 30 元,积分应该是 30(按 1 元 1 分),余额 70。如果对不上,先查 ConsumeRecords 里有没有漏记,再查 UPDATE 语句的 WHERE 条件是不是写错了。
我自己的习惯是:每改完一个功能,先在数据库里手动执行一遍对应的 SQL,确认结果正确,再写 C# 代码。这样出问题时能快速定位是 SQL 错了还是代码错了。课程设计不需要多高深的技术,但把事务、参数化查询和分层结构讲清楚,已经能超过大部分同学。希望帮到你。
本文还有配套的精品资源,点击获取