ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ASP.NET供应商管理系统:从三层架构到部署避坑指南

ASP.NET供应商管理系统:从三层架构到部署避坑指南 简介这套ASP.NET供应商管理系统以C#语言开发基于ASP.NET MVC框架构建面向需要完成毕业设计或课程项目的计算机专业学生也适合企业信息化人员了解供应链后台的实现方式。系统整合了供应商分类管理、基础信息维护、评价管理、留言管理和用户权限管理五大模块可帮助企业高效维护供应商档案、评估合作表现并控制访问权限从而优化供应链协作流程。压缩包共386个文件约1.77MB其中包含12个cs后端核心代码、7个aspx页面、90个js交互脚本、26个css样式、173个gif界面素材以及mdf和ldf数据库文件便于直接部署或二次开发。目前已有386人学习下载。通过完整源码和数据库结构读者能理解供应商管理系统的分层设计与CRUD实现掌握登录验证、文件上传、留言交互等典型功能的编码思路并可将项目作为快速搭建同类管理系统的实用模板。代码结构清晰界面素材齐备是理解ASP.NET项目分层、数据库操作和权限验证的直观案例。1. ASP.NET供应商管理系统毕业设计可以直接改的东西如果你的毕业设计题目是“供应商管理系统”技术栈限定在ASP.NET C# SQL Server那你大概率已经在网上翻了好几天。要么找到的是十几年前的源码界面还是表格套表格要么下载下来缺数据库脚本根本跑不起来要么代码写得像天书答辩老师问两句就露馅。这套资源就是为了这个场景准备的基于ASP.NET WebForms的完整供应商管理系统包含供应商档案、产品资料、采购订单、用户登录与权限、报表打印这些毕业设计里出现频率最高的模块。数据库用的SQL Server数据访问采用三层架构代码风格接近课堂教法改起来不费劲。适合C#方向需要交毕设的学生也适合刚入职想熟悉老式WebForms项目怎么运转的初级开发。2. 系统骨架三层架构与关键配置文件2.1 为什么选三层架构而不是直接拖控件做管理系统答辩老师最爱问的就是“你这个项目分层了吗”。这套资源采用的是经典的三层UI层负责页面展示业务层放逻辑判断数据访问层统一管数据库操作。好处是每层都能单独改比如换数据库、改业务规则不会把页面文件搞成一坨。很多新手拿到源码后习惯直接在.aspx.cs里写SqlConnection当时跑得通但一旦数据访问逻辑变多页面代码全是数据库操作维护成本立刻上去了。数据访问层在这套资源里集中在DAL文件夹下每个实体对应一个类比如SupplierDAL、ProductDAL、OrderDAL。业务层在BLL文件夹下调用DAL方法再做一些非空判断、权限判断这类事情。UI层只跟BLL打交道。这样的结构对毕业设计来说足够交代了答辩时你讲“业务逻辑和界面分离”也站得住。2.2 Web.config连接字符串与DbHelper.cs任何配置文件里最重要的就是连接字符串。这套资源的连接字符串写在Web.config的connectionStrings节点下我在自己的机器上是这样配的connectionStrings add nameSupplierDB connectionStringData Source.;Initial CatalogSupplierDB;User IDsa;Password123456;MultipleActiveResultSetsTrue providerNameSystem.Data.SqlClient / /connectionStrings注意Data Source.表示本机的默认实例Initial Catalog是数据库名。如果你自己的SQL Server是命名实例比如localhost\SQLEXPRESS这里要改成Data Sourcelocalhost\SQLEXPRESS。MultipleActiveResultSets建议保留后面做GridView分页和同时打开多个DataReader时会用到不写这个在某些操作下会报“已有打开的与此连接相关联的DataReader”。资源里的数据访问基类DbHelper.cs封装了最常用的增删改查方法核心就是三件套public static SqlConnection GetConnection() { string connStr ConfigurationManager.ConnectionStrings[SupplierDB].ConnectionString; return new SqlConnection(connStr); } public static int ExecuteNonQuery(string sql, params SqlParameter[] parameters) { using (SqlConnection conn GetConnection()) { using (SqlCommand cmd new SqlCommand(sql, conn)) { if (parameters ! null) { cmd.Parameters.AddRange(parameters); } conn.Open(); return cmd.ExecuteNonQuery(); } } } public static SqlDataReader ExecuteReader(string sql, params SqlParameter[] parameters) { SqlConnection conn GetConnection(); SqlCommand cmd new SqlCommand(sql, conn); if (parameters ! null) { cmd.Parameters.AddRange(parameters); } conn.Open(); return cmd.ExecuteReader(CommandBehavior.CloseConnection); }ExecuteReader里有个细节容易踩坑返回的是SqlDataReader但调用方必须记得using或者等它读完就关闭否则连接一直占着不放。所以它加了CommandBehavior.CloseConnection意思是当Reader关闭时底层的SqlConnection也跟着关闭这个技巧比你在页面里手动conn.Close()要更不容易漏。参数化查询全部用SqlParameter传值字符串拼接只会在你自己新写的代码里出现原作里没有一处是拼SQL的这一点在答辩时也值得提一下。2.3 Global.asax里的统一异常处理开发时你会在Visual Studio里看到黄色的报错页面但部署到老师的机器上很多服务器默认会把错误挡掉页面显示一片空白。这个资源在Global.asax里写了一个全局异常捕获把错误信息记录到日志文件void Application_Error(object sender, EventArgs e) { Exception ex Server.GetLastError(); if (ex ! null) { string logPath Server.MapPath(~/Logs/error_ DateTime.Now.ToString(yyyyMMdd) .txt); System.IO.File.AppendAllText(logPath, DateTime.Now.ToString(yyyy-MM-dd HH:mm:ss) ex.ToString() Environment.NewLine); } }这样的处理方式不算高级但对毕业设计场景很实用。一旦答辩演示时页面白屏你可以直接翻开Logs目录下的日志看具体异常而不是当着老师的面打开Visual Studio调试。Server.MapPath用来把虚拟路径转成物理路径AppendAllText确保日志是追加写入而不是覆盖。很多类似资源没有这一层拿到手后建议自己保留它实际项目里它比想象的管用。3. 数据字典与采购流程五张表怎么串起来3.1 供应商、产品、订单、订单明细、用户五张核心表供应商管理系统说到底就是围绕“供应商有哪些产品、我们向哪个供应商采购了什么东西、价格是多少”来转的。这套资源的数据字典里一共五张表其中订单和订单明细拆成两张是为了满足1对多的数据关系——一张采购单可以包含多种不同产品。下面这个建表脚本基本覆盖了核心结构CREATE TABLE [dbo].[SupplierInfo]( [SupplierID] [int] IDENTITY(1,1) PRIMARY KEY, [SupplierNo] [nvarchar](20) NOT NULL UNIQUE, [SupplierName] [nvarchar](100) NOT NULL, [ContactPerson] [nvarchar](50) NULL, [Phone] [nvarchar](20) NULL, [Address] [nvarchar](200) NULL, [MainProduct] [nvarchar](200) NULL, [CooperationStatus] [nvarchar](10) NOT NULL DEFAULT N合作中 ) GO CREATE TABLE [dbo].[ProductInfo]( [ProductID] [int] IDENTITY(1,1) PRIMARY KEY, [ProductNo] [nvarchar](20) NOT NULL UNIQUE, [ProductName] [nvarchar](100) NOT NULL, [Specification] [nvarchar](50) NULL, [Unit] [nvarchar](10) NULL, [UnitPrice] [decimal](18,2) NOT NULL, [SupplierID] [int] NULL FOREIGN KEY REFERENCES [dbo].[SupplierInfo]([SupplierID]) ) GO CREATE TABLE [dbo].[PurchaseOrder]( [OrderID] [int] IDENTITY(1,1) PRIMARY KEY, [OrderNo] [nvarchar](20) NOT NULL, [SupplierID] [int] NOT NULL FOREIGN KEY REFERENCES [dbo].[SupplierInfo]([SupplierID]), [OrderDate] [datetime] NOT NULL DEFAULT GETDATE(), [TotalAmount] [decimal](18,2) NOT NULL DEFAULT 0 ) GO CREATE TABLE [dbo].[OrderDetail]( [DetailID] [int] IDENTITY(1,1) PRIMARY KEY, [OrderID] [int] NOT NULL FOREIGN KEY REFERENCES [dbo].[PurchaseOrder]([OrderID]), [ProductID] [int] NOT NULL FOREIGN KEY REFERENCES [dbo].[ProductInfo]([ProductID]), [Quantity] [int] NOT NULL, [Price] [decimal](18,2) NOT NULL ) GO CREATE TABLE [dbo].[SysUser]( [UserID] [int] IDENTITY(1,1) PRIMARY KEY, [UserName] [nvarchar](50) NOT NULL UNIQUE, [Password] [nvarchar](50) NOT NULL, [RoleName] [nvarchar](20) NOT NULL DEFAULT N普通用户 ) GOSupplierNo和ProductNo是业务编号不是主键自增值这个命名要知道。在实际业务里供应商编号往往是手填的有规律的编码比如SUP001而SupplierID是内部自增主键两者职责不同。如果你改成全靠自增ID显示给用户答辩时老师可能会问业务编号的意义。CooperationStatus字段在设计时用默认值“合作中”你可以自己加个下拉框去筛选暂停合作或已终止的供应商。3.2 供应商管理页面的GridView数据绑定供应商列表页面是这套资源最核心的展示页面它用的是WebForms时代最常见的GridView控件。关键在于DataSourceID指向一个SqlDataSource还是用后置代码手动绑定两种方式显示效果一样但答辩时区别很大。这个资源采用的是在Page_Load里调用BLL层方法再绑定的方式逻辑链路比较完整protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { BindSupplierList(); } } private void BindSupplierList() { SupplierManager manager new SupplierManager(); DataTable dt manager.GetAllSuppliers(); GridView1.DataSource dt; GridView1.DataBind(); }IsPostBack检查一定要写。如果不判断每次点击GridView里的翻页或编辑按钮页面都会重新加载一遍数据又重新绑定你会发现点击翻页根本无效果。DataTable作为中间载体比直接用List更贴合WebForms时代的习惯因为GridView的列绑定可以直接写Eval(SupplierName)这种表达式换成实体集合也差不多但DataTable在快速开发时更顺手。分页开启分两步页面标签里的AllowPagingTrue和PageSize10然后事件里指定当前页索引。很多新手只设置了AllowPaging但忘了处理PageIndexChanging事件结果就是翻页按钮出现但点击后永远回到第一页protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e) { GridView1.PageIndex e.NewPageIndex; BindSupplierList(); }这里GridView1.PageIndex会被请求回发时保存的状态覆盖所以必须在事件里重新赋值。e.NewPageIndex拿到的是你点击的页码索引。赋值之后重新调用BindSupplierList()数据表重新查询GridView就跳到对应的页。PageSize我一般设成10一页十条记录正好在屏幕上显示得完翻页也不用翻太多次。3.3 采购单保存时的事务处理采购订单的保存是这个资源里最有技术含量的一个点。订单主表写入一条记录拿到自增的OrderID再把明细数据逐条写入订单明细表。这里最大的坑是如果明细里有一条写失败主表数据已经入库最后就会产生一张没有明细的“空洞”订单。源码里用了SqlTransaction来包住整个写入过程using (SqlConnection conn DbHelper.GetConnection()) { conn.Open(); SqlTransaction tran conn.BeginTransaction(); try { string sqlOrder INSERT INTO PurchaseOrder(OrderNo, SupplierID, OrderDate, TotalAmount) VALUES(OrderNo, SupplierID, GETDATE(), TotalAmount); SELECT SCOPE_IDENTITY();; SqlCommand cmd new SqlCommand(sqlOrder, conn, tran); cmd.Parameters.AddWithValue(OrderNo, orderNo); cmd.Parameters.AddWithValue(SupplierID, supplierId); cmd.Parameters.AddWithValue(TotalAmount, totalAmount); int orderId Convert.ToInt32(cmd.ExecuteScalar()); string sqlDetail INSERT INTO OrderDetail(OrderID, ProductID, Quantity, Price) VALUES(OrderID, ProductID, Quantity, Price); foreach (var detail in detailList) { SqlCommand cmdDetail new SqlCommand(sqlDetail, conn, tran); cmdDetail.Parameters.AddWithValue(OrderID, orderId); cmdDetail.Parameters.AddWithValue(ProductID, detail.ProductID); cmdDetail.Parameters.AddWithValue(Quantity, detail.Quantity); cmdDetail.Parameters.AddWithValue(Price, detail.Price); cmdDetail.ExecuteNonQuery(); } tran.Commit(); } catch { tran.Rollback(); throw; } }注意看SCOPE_IDENTITY()的用法。它是取当前会话里最后插入的自增ID和IDENTITY的区别在于不会拿到触发器生成的ID。在这个场景里必须用SCOPE_IDENTITY()才能拿到刚插入的OrderID否则如果表上有触发器你可能会取到触发器插入的那条记录的自增值。事务的作用说白了就是“要么全有要么全无”主表和明细任何一个环节抛异常Rollback会把已经写入的主表数据撤销这是保证数据一致性的关键逻辑。答辩时老师问到“数据怎么保证不出现脏数据”直接答这条就行。4. 部署避坑从开发机到答辩机的五个常见问题4.1 GridView的编辑按钮点了没反应现象页面上的“编辑”链接点击后行没有进入编辑状态或者干脆报了未处理的异常。原因绝大多数情况是DataKeyNames没有设置。GridView的编辑功能靠主键字段来识别到底是哪一行你不指定它控件不知道用什么去更新数据。解决在GridView的标记里加上DataKeyNamesSupplierID然后在RowEditing事件里调用Gridview1.EditIndex e.NewEditIndex;再重新绑定数据。我已经见过不下五次这种问题十个里有九个都是忘了DataKeyNames。4.2 连接字符串在别人电脑上报“System.Data.SqlClient.SqlException”现象在自己电脑上跑得好好的拷到答辩教室的机器上一打开页面就报无法连接数据库。原因Data Source.在这台机器上可能没有默认实例或者SQL Server服务没启动甚至根本没有安装SQL Server。解决发布前问清楚答辩机器的数据库环境。如果是命名实例改成Data Source机器名\\实例名如果是远程数据库写成IP地址。我习惯在Web.config里把这个节点写得醒目一点并在注释里标注三种写法演示当天换着试。还有一个隐蔽点User ID和Password用的是SQL Server身份验证如果目标机器开的是Windows身份验证要先去数据库里启用混合认证。4.3 发布后提示“无法加载文件或程序集”现象在本机编译运行没问题发布到IIS后访问页面报错“CS0001 无法加载文件或程序集Newtonsoft.Json”或类似信息。原因有些程序集没有被自动复制到bin目录尤其是那种在代码里引用但在界面层没有直接使用的库VS发布时可能漏掉。解决在项目里找到引用把引用的Copy Local属性设为True发布后打开服务器的bin目录看一遍DLL是否齐全。毕业设计里常用的第三方库就那几样最稳的办法是把整个bin目录手动拷到服务器上覆盖省得一堆玄学问题。4.4 页面中文显示乱码现象页面标题和按钮文字正常但数据库里的中文显示为“??”或一长串乱码。原因一是数据库列类型是varchar而不是nvarchar另一个是写入时SQL语句里的中文字符串没有加N前缀。解决建表时统一用nvarchar插入时写成N供应商A。如果历史表已经建好了用ALTER TABLE SupplierInfo ALTER COLUMN SupplierName NVARCHAR(100)改字段类型。这套资源的建表脚本全用nvarchar你自己新加字段时也记得照着写。4.5 IIS程序池配置不对导致错误代码500.19现象在IIS里部署完成后浏览页面报“500.19 配置错误无法读取配置文件”。原因IIS程序池选错了版本或者集成和经典模式选错。解决方案在应用程序池里确认干路.NET CLR版本选的是.NET v4.0.30319托管管道模式选“集成Integrated”。很多老资源选的是经典模式因为老项目有HttpModule处理方式依赖经典模式但现代IIS默认对集成模式支持得更好。如果在“集成”模式下个别功能不正常需要回退到经典才去切换。5. 验收与进阶答辩演示清单与Framework到Core的迁移5.1 答辩演示前必须过一遍的完整流程演示最容易翻车的不是功能缺失而是你自己忘了录入数据的顺序。下面这个清单是我自己当年答辩前反复走的建议你也按这个顺序在部署好的机器上完整跑一遍步骤操作方法预期结果登录输入admin/123456跳转到主页面显示用户名新增供应商填写编号、名称、联系人、电话保存成功列表出现记录模糊查询用供应商名称关键字搜索只返回匹配的记录编辑供应商点击行内编辑改电话并保存列表中电话更新新增产品为刚创建的供应商挂一个产品产品列表显示供应商名称创建采购单从供应商列表选择添加产品明细订单主表和明细同时写入删除订单删除刚创建的采购单明细同时删除无残留退出登录点击退出按钮跳回登录页每一步之间注意顺序特别是“先建供应商再建产品再建订单”因为外键存在没有供应商就没法创建产品没有产品就没法创建订单明细。如果演示时直接从订单开始操作会一直看到下拉框是空的。5.2 从ASP.NET Framework迁到ASP.NET Core MVC的加减法这套资源是ASP.NET Framework WebForms的写法如果你想要技术栈新一点把它改成ASP.NET Core MVC也不是不行但要清楚Webster的四个对应关系。aspx页面 aspx.cs的模型在Core里完全不存在页面逻辑变成了Controller加ViewGridView和SqlDataSource这些服务器控件消失了前端显示换成了表格配合Razor语法Session用法一样但底层存储方式变了默认是内存模式Web.config里的配置绝大多数被appsettings.json取代。代码迁移时最容易照搬的反而是业务层和数据访问层因为那部分就是普通的C#类只要把ConfigurationManager换成IConfiguration就能用。UI层需要重写但核心功能逻辑是现成的。如果你打算在论文里写“本系统采用ASP.NET Core MVC架构”那就要在框架选型里交代清楚。我在最后一次答辩演练的时候犯过一个低级失误演示前把SQL Server服务给关了结果打开登录页面直接白屏。从那以后我每次做演示或交付前都会先检查数据库服务运行状态、连接字符串是否匹配、IIS程序池是否正常再走一遍完整操作流程。这套检查习惯后来也带到了实际的项目交付里希望帮到你。本文还有配套的精品资源点击获取
返回列表