资讯动态

C#库存管理系统源码解析:从Excel到数据库的仓库管理实践

发布时间:2026/10/26 11:28:54 来源:尧图企业网站定制
简介这份资源是一套基于C#与SQL Server开发的库存管理系统完整工程面向学习WinForm桌面开发与数据库应用的学生、初级开发者以及需要搭建仓储管理原型的企业技术人员。压缩包共194个文件约3.98MB以56个cs源代码文件为核心配合26个resx与resources资源文件、17个ico图标、42张jpg界面素材另有7个sql脚本、mdf与ldf数据库文件及bak备份并附使用说明书doc覆盖商品管理、出入库记录、库存查询与报表生成等模块。已有719人学习下载读者可从中获得可运行的解决方案、数据库建表脚本与操作文档通过阅读主窗体与数据操作类代码理解业务分层与增删改查实现也可直接部署调试、按需扩展功能是兼顾入门学习与二次开发的实践素材。1. 从一张 Excel 库存表说起这套 C# 仓库管理系统能替你扛下什么我见过太多小仓库的日常入库靠手写单出库靠微信群喊一声月底盘点是三个人围着几张 Excel 对到凌晨最后还差两百件货对不上。这套 C# 库存管理系统源代码解决的正是这个场景——它把商品档案、入库、出库、库存流水、库存预警这几件事收进一个桌面程序里数据落在数据库而不是散落的表格里。技术栈是 C# WinForms或 WPF 关系型数据库典型的 .NET 桌面三层结构适合两类人一是想拿一套能跑的业务系统练手、改造成自己公司内部工具的开发者二是需要一套带数据库脚本和说明文档、能直接部署到局域网内几台电脑上用的中小团队。下面我按「它是什么 → 怎么跑起来 → 核心代码怎么读 → 怎么改」的顺序拆一遍。2. 技术选型与目录结构一套桌面库存系统是怎么搭起来的2.1 为什么是 C# WinForms 关系型数据库这类库存管理系统的需求特征很明确单机或局域网内多客户端、数据量在十万级以内、操作人员是仓库管理员而不是技术人员、需要打印单据和导出报表。C# 桌面端在这个场景下几乎是性价比最高的选择——开发快、控件成熟、部署简单装个 .NET 运行时就能跑不需要配 Web 服务器。数据库层常见做法是 SQL ServerExpress 版免费或 MySQL两者在这套代码里通常通过一个数据库访问层隔离换库只改连接字符串和少量 SQL 方言。我一般会先看项目里有没有独立的DAL或Helper目录有的话说明作者做了分层二次开发会舒服很多。提示如果拿到的是 SQL Server 版本而你的环境只有 MySQL别急着改代码先确认数据访问层是否用了参数化 SQL 而非存储过程前者迁移成本低得多。2.2 典型目录结构长什么样一套完整的库存管理系统源码目录大致会是这样不同作者命名略有差异但骨架相似InventorySystem/ ├── InventorySystem.sln # 解决方案文件 ├── DB/ │ ├── CreateDatabase.sql # 建库建表脚本 │ └── InitData.sql # 初始数据管理员账号、基础分类 ├── InventorySystem.UI/ # 界面层 │ ├── Forms/ │ │ ├── LoginForm.cs │ │ ├── MainForm.cs │ │ ├── ProductForm.cs # 商品档案 │ │ ├── InboundForm.cs # 入库单 │ │ ├── OutboundForm.cs # 出库单 │ │ └── StockQueryForm.cs # 库存查询与预警 │ └── Program.cs ├── InventorySystem.BLL/ # 业务逻辑层 │ ├── ProductService.cs │ ├── StockService.cs │ └── ReportService.cs ├── InventorySystem.DAL/ # 数据访问层 │ ├── SqlHelper.cs │ └── ProductDao.cs ├── InventorySystem.Model/ # 实体类 │ └── Product.cs / StockRecord.cs └── Docs/ └── 使用说明.docx # 部署与操作文档这个分层不是摆设。UI 层只管控件和事件BLL 层管业务规则比如出库时库存够不够、单价怎么算DAL 层管 SQLModel 层是纯数据载体。二次开发时你要加一个「调拨单」功能改动集中在 Model 加实体、DAL 加 DAO、BLL 加服务、UI 加窗体四层各加一块互不污染。2.3 数据库表设计库存系统的地基库存系统的核心表通常就五六张理解它们的关系比读代码更重要表名作用关键字段Product商品档案ProductId、Name、CategoryId、Unit、SafetyStockCategory商品分类CategoryId、CategoryNameInboundOrder入库单主表OrderId、OrderNo、SupplierId、InDate、OperatorInboundDetail入库单明细DetailId、OrderId、ProductId、Qty、PriceOutboundOrder / OutboundDetail出库单主表与明细同上结构StockRecord库存流水RecordId、ProductId、ChangeQty、Type、CreateTimeUser操作员UserId、UserName、PasswordHash、Role设计上的关键点是库存数量不直接存在 Product 表里当唯一真相而是由 StockRecord 流水累加得出或者用一张 Stock 表做冗余汇总、由触发器或业务代码维护。前者查询慢但可追溯后者快但有一致性风险。这套代码多半采用「Stock 汇总表 StockRecord 流水表」双写入库出库时在同一个事务里更新两张表。注意如果你接手后发现库存数和流水对不上八成是某次操作只写了流水没更新汇总表或者事务没包住。排查时先跑一遍「按流水重算库存」的校验 SQL。3. 把项目跑起来从建库到登录的完整步骤3.1 环境准备与依赖确认跑这套系统需要三样东西.NET 开发环境Visual Studio 2019 及以上或 Rider、一个关系型数据库实例、以及项目里那份建库脚本。先打开.sln看目标框架是 .NET Framework 4.x 还是 .NET 6/8这决定了你装哪个运行时。# 确认本机 .NET SDK 版本和项目目标框架对齐 dotnet --list-sdks # 如果用 MySQL确认服务在跑 mysql --version如果项目是 .NET Framework 4.8 的老项目用 Visual Studio 打开最省事NuGet 还原会自动处理packages.config里的依赖。若是 SDK 风格项目命令行dotnet restore即可。这一步翻车的典型原因是目标框架和本机 SDK 不匹配报错信息里会明确写「当前 SDK 不支持 targetFramework」照着改项目文件里的TargetFramework或补装对应 SDK 就行。3.2 建库与初始化数据打开DB/CreateDatabase.sql先看它有没有CREATE DATABASE语句。有的话直接整段执行没有的话你得先手动建库再执行建表部分。-- 建库如果脚本里没有手动补这一段 CREATE DATABASE InventoryDB DEFAULT CHARACTER SET utf8mb4; USE InventoryDB; -- 商品表SafetyStock 是安全库存低于它触发预警 CREATE TABLE Product ( ProductId INT PRIMARY KEY AUTO_INCREMENT, Name VARCHAR(100) NOT NULL, CategoryId INT NOT NULL, Unit VARCHAR(20), SafetyStock INT DEFAULT 0, Status TINYINT DEFAULT 1 ); -- 库存流水Type 区分入库/出库/盘点调整 CREATE TABLE StockRecord ( RecordId BIGINT PRIMARY KEY AUTO_INCREMENT, ProductId INT NOT NULL, ChangeQty INT NOT NULL, -- 入库为正出库为负 Type VARCHAR(20) NOT NULL, CreateTime DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_product (ProductId) );执行完建表脚本再跑InitData.sql它会插入默认管理员账号和几个基础分类。这里有个血泪经验初始密码在脚本里往往是明文或简单哈希登录后第一件事就是改掉别让默认账号留在生产环境里。3.3 改连接字符串并首次启动数据库访问层的连接字符串一般集中在App.config.NET Framework或appsettings.json.NET Core里也可能硬编码在SqlHelper.cs顶部。找到它改成你本机的实例地址、库名、账号密码。!-- App.config 里的典型配置 -- connectionStrings add nameInventoryDB connectionStringServerlocalhost;DatabaseInventoryDB;Uidroot;Pwd你的密码; providerNameMySql.Data.MySqlClient / /connectionStrings改完编译运行出现登录窗体说明链路通了。用初始化脚本里的管理员账号登录进主界面后先做三件事验证系统是否正常新建一个商品分类、录入一个商品、做一笔入库。三件事都成功且库存查询能看到数字变化说明 UI→BLL→DAL→DB 整条链路是通的。提示首次运行报「找不到表」多半是库名或字符集不对报「连接超时」先确认数据库服务监听地址别一头扎进代码里找。4. 核心模块拆解入库、出库与库存预警的代码链路4.1 数据访问层一个 SqlHelper 撑起所有 SQL这套代码里最值得先读的是SqlHelper.cs它是所有 DAO 的底座。典型实现封装了连接管理、参数化查询和事务支持public static class SqlHelper { private static readonly string ConnStr ConfigurationManager.ConnectionStrings[InventoryDB].ConnectionString; // 执行增删改返回受影响行数params 防注入 public static int ExecuteNonQuery(string sql, params SqlParameter[] paras) { using (var conn new SqlConnection(ConnStr)) using (var cmd new SqlCommand(sql, conn)) { if (paras ! null) cmd.Parameters.AddRange(paras); conn.Open(); return cmd.ExecuteNonQuery(); } } // 查询返回 DataTable供界面绑定 DataGridView public static DataTable ExecuteQuery(string sql, params SqlParameter[] paras) { using (var conn new SqlConnection(ConnStr)) using (var cmd new SqlCommand(sql, conn)) using (var adapter new SqlDataAdapter(cmd)) { if (paras ! null) cmd.Parameters.AddRange(paras); var dt new DataTable(); adapter.Fill(dt); return dt; } } }逻辑说明using保证连接和命令对象用完即释放这是桌面程序里防止连接池耗尽的基本功。params SqlParameter[]让调用方传参而不是拼字符串直接堵住 SQL 注入。参数说明sql是带参数名占位符的语句paras是对应参数数组。二次开发时新增 DAO 方法照这个模式写就行别图省事用字符串拼接——我见过一个项目因为商品名里带单引号导致整张入库单保存失败排查了半天。4.2 入库业务一个事务里同时写三张表入库是库存系统里最典型的写操作它要同时完成写入库单主表、写明细、更新库存汇总、写流水。这四步必须在一个事务里否则中途失败就会出现「单子有了货没进」的脏数据。public bool CreateInbound(InboundOrder order, ListInboundDetail details) { using (var conn new SqlConnection(ConnStr)) { conn.Open(); using (var tran conn.BeginTransaction()) // 开启事务 { try { // 1. 插入主表拿回自增 OrderId int orderId InsertOrder(conn, tran, order); foreach (var d in details) { d.OrderId orderId; InsertDetail(conn, tran, d); // 2. 插明细 UpdateStock(conn, tran, d.ProductId, d.Qty); // 3. 加库存 InsertRecord(conn, tran, d.ProductId, d.Qty, 入库); // 4. 写流水 } tran.Commit(); // 全部成功才提交 return true; } catch (Exception ex) { tran.Rollback(); // 任一步失败整体回滚 LogError(ex); return false; } } } }逻辑说明BeginTransaction到Commit之间任何一步抛异常都会走Rollback保证四张表要么全改要么全不改。参数说明order是主单信息单号、供应商、操作员details是明细列表每条的Qty为正数表示入库数量。这里有个容易忽略的点UpdateStock应该用UPDATE Stock SET Qty Qty qty这种数据库端自增而不是先查再算再写后者在并发下会丢更新。4.3 出库与库存校验先扣减还是先校验出库比入库多一道关卡库存够不够。常见做法是在 BLL 层先查当前库存够则继续不够直接返回提示。但严格来说查询和扣减之间仍有并发窗口稳妥做法是把校验写进 UPDATE 的条件里// 条件更新只有库存足够时才扣减返回受影响行数 public int DeductStock(SqlConnection conn, SqlTransaction tran, int productId, int qty) { string sql UPDATE Stock SET Qty Qty - qty WHERE ProductId pid AND Qty qty; using (var cmd new SqlCommand(sql, conn, tran)) { cmd.Parameters.AddWithValue(qty, qty); cmd.Parameters.AddWithValue(pid, productId); return cmd.ExecuteNonQuery(); // 返回 0 表示库存不足 } }逻辑说明WHERE Qty qty把校验和扣减合成一条原子语句返回 0 就说明库存不够业务层据此回滚整个出库事务并提示「库存不足」。参数说明qty传正数SQL 里做减法。这种写法比「先 SELECT 再判断再 UPDATE」少一次往返也避免了并发超卖。如果项目里用的是先查后改的老写法二次开发时建议顺手改掉。4.4 库存预警一条 SQL 搞定低库存清单预警功能本质就是查Stock.Qty Product.SafetyStock的商品。界面层通常用一个 DataGridView 绑定查询结果定时刷新或进页面时刷新一次。-- 低库存清单联表查商品名、当前库存、安全库存 SELECT p.ProductId, p.Name, s.Qty, p.SafetyStock, (p.SafetyStock - s.Qty) AS Shortage FROM Product p JOIN Stock s ON p.ProductId s.ProductId WHERE s.Qty p.SafetyStock AND p.Status 1 ORDER BY Shortage DESC;Shortage是缺口数量按它倒序排最急的排最前。p.Status 1过滤掉停用商品避免已下架商品一直挂在预警列表里。这个查询在万级商品量下毫秒级返回不需要额外优化如果商品上了十万级给Stock.Qty和Product.SafetyStock建联合索引即可。5. 避坑与常见问题跑不起来时先看这几条5.1 现象登录成功但主界面所有列表都是空的原因通常是数据库连上了、表也建了但InitData.sql没执行或者执行到了错误的库。解决连上数据库执行SELECT COUNT(*) FROM Product为 0 就补跑初始化脚本同时确认连接字符串里的Database和你建库时的库名完全一致大小写敏感的环境下差一个字母都不行。5.2 现象入库保存时报「事务已完成不能再使用」原因是代码里在Commit之后又用了同一个事务对象或连接对象常见于把日志写入也塞进了事务块尾部。解决把提交后的操作移出using (tran)块或者用独立连接写日志。这类错误堆栈会明确指向SqlTransaction照着堆栈找到提交点后面的代码即可。5.3 现象库存数和流水对不上越用差得越多原因是某次操作只更新了汇总表没写流水或者反过来也可能是历史数据被手工改过。解决写一条校验 SQL按StockRecord分组求和和Stock.Qty比对找出差异商品再决定以流水为准重算还是人工核对。长期方案是把「更新汇总 写流水」强制放进同一个事务代码评审时重点看这一点。5.4 现象多台电脑同时出库偶尔出现负库存原因是出库用了「先查后改」的写法两个客户端同时查到库存 10、各出 8都通过了校验。解决改成 4.3 节那种条件更新WHERE Qty qty靠数据库行锁保证原子性。改完后压测一下开两个客户端同时对同一商品出库看是否只有一个成功。5.5 现象发布到客户机器上提示缺少 DLL 或运行时原因是目标机器没装对应版本的 .NET 运行时或者项目引用了本机 GAC 里的程序集。解决优先用自包含发布dotnet publish --self-contained把运行时打进去老 .NET Framework 项目则在安装包里附带运行时安装程序。发布前在干净虚拟机上跑一遍别在开发机上验证——开发机什么都有测不出问题。6. 二次开发改造从「能用」到「贴合自己业务」拿到这套代码多数人最终都会改三处加字段、加单据类型、换报表。我一般按这个顺序动手风险从低到高。第一步加商品扩展字段。比如要记录品牌和规格改动路径是Product表加两列 →Model/Product.cs加两个属性 →ProductDao的增删改查 SQL 补上字段 →ProductForm加两个 TextBox 并绑定。四层各改一处改完跑一遍商品的新增编辑删除确认没有遗漏的 SQL。第二步加一种单据类型。比如「调拨单」本质是「出库 入库」的组合。最省事的做法是复用现有入库出库服务在 BLL 层写一个TransferService内部调两次库存变更包在同一个事务里。别去复制粘贴一套新的 DAO那样以后改库存逻辑要改三处。第三步换报表或加导出。库存系统的报表需求千变万化建议把查询 SQL 和导出逻辑分离查询返回DataTable导出用现成库如 NPOI 导 Excel统一处理。这样业务方要改报表口径只动 SQL 不动导出代码。改造后的验证方法我固定用一套造 3 个商品、各做 2 笔入库 2 笔出库、跑一次盘点调整然后核对流水总和是否等于汇总库存、预警清单是否只包含低于安全库存的商品、导出的 Excel 行数是否和查询结果一致。这套用例十分钟能跑完但能挡住八成回归问题。有个习惯是踩坑踩出来的每次改完数据库脚本我一定在空库上从头执行一遍CreateDatabase.sql InitData.sql而不是在已有数据的库上增量改。因为增量改容易漏等交付到客户那里建新库时才炸那时候排查成本翻好几倍。从那以后我每次交付前都强制走一遍「空库全量建库 全流程冒烟」希望这个习惯也能帮到你。这套带数据库脚本和说明文档的 C# 库存管理系统源码拿来当二次开发的起点是够用的先把环境跑通再按上面的顺序小步改比一上来大动骨架稳得多。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑