ARTICLE DETAIL

资讯详情

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

ASP.NET+SQL Server项目管理系统实战搭建

ASP.NET+SQL Server项目管理系统实战搭建 简介这是一套基于ASP.NET Web Forms架构的项目管理信息系统源码面向C#初学者与Web开发入门者适用于学习B/S系统设计、三层架构实践及Access数据库操作。资源完整包含前端页面.aspx、后台逻辑.cs、配置文件.config、数据库projects.mdb及Visual Studio解决方案.sln覆盖用户权限控制、项目全生命周期管理、日志记录与数据库维护等典型业务场景。压缩包共297个文件含96个C#源码文件、28个ASPX页面、50个DLL依赖库、10个CSProj工程文件及2个Access数据库文件总大小5.09MB结构清晰模块命名规范如Manage_projects.aspx、Manage_history.aspx等便于按角色功能快速定位代码。已有505人学习下载可直接在VS2010中运行调试附带管理员账号admin/1与详细登录路径Web/login.aspx是理解权限分级、Web表单交互与轻量级数据库集成的实用教学案例。1. 为什么用 ASP.NET SQL Server 搭建项目管理系统比用低代码平台更稳、更可控、更适合中型团队落地你手头有个 515 人的研发/实施团队正在接政府或制造业客户的定制化项目管理需求要管任务拆解、工时填报、里程碑评审、附件归档、角色权限分级比如PM能看全部开发只能看自己任务还要和现有OA单点登录对接。这时候有人提议“用钉钉宜搭/飞书多维表格快速上线”但你心里清楚——三个月后客户突然要加“按合同金额自动分摊人力成本”、“导出符合财政审计格式的Excel”、“对接PLM系统BOM结构树”低代码平台立刻卡死二次开发黑盒难调运维日志查不到SQL执行链路连慢查询都定位不了。而 ASP.NETFramework 4.7.2 或 .NET 6 SQL Server2016这套组合不是“过时的老古董”恰恰是在真实企业环境中被反复验证过的稳态架构C# 写业务逻辑清晰可调试Visual Studio 提供全链路断点SQL Profiler内存快照SQL Server 的索引优化、分区表、AlwaysOn 故障转移能力让数据层扛得住并发写入和复杂报表。它不炫技但每一步你都能看见、改得动、压得稳。本文就带你从零跑通一个最小可行的项目管理系统骨架VS 创建 Web Forms 或 MVC 项目 → 连上本地 SQL Server 实例 → 建好 Projects/Tasks/Users 三张核心表 → 用 Entity Framework Code First 自动生成 CRUD → 在页面上增删改查任务项。所有步骤基于 VS 2022 SQL Server 2019兼容 2016/2022不依赖任何第三方模板或脚手架命令、配置、SQL 脚本全部可复制粘贴。2. 用 Visual Studio 2022 创建 ASP.NET Web Forms 项目并连接 SQL Server 数据库2.1 新建项目选对模板是避免后续踩坑的第一步打开 Visual Studio 2022确保已安装ASP.NET and web development工作负载点击「创建新项目」→ 在搜索框输入ASP.NET Web Forms→ 选择ASP.NET Web Forms App (.NET Framework)模板注意不是.NET Core或.NET 5因为标题明确是传统 ASP.NET且大量存量客户仍运行在 .NET Framework 上。提示不要选ASP.NET Core Web App虽然技术更新但标题指定的是 ASP.NET即 .NET Framework 版本其 Web Forms 生命周期模型、ViewState 机制、服务器控件事件绑定方式与 Core 完全不同。强行混用会导致Page_Load不触发、asp:Button点击无响应等玄学问题。设置项目名称如ProjectManagerWeb、位置点击「创建」。VS 会自动生成包含Default.aspx、Site.Master、App_Code等标准目录的解决方案。此时先不要急着写代码先确认数据库连接环境是否就绪。2.2 配置 SQL Server 连接字符串本地实例名、身份验证模式必须匹配本方案默认使用Windows 身份验证最简、最安全无需维护密码前提是你的 SQL Server 已安装且服务正在运行。打开 SQL Server Configuration Manager → 确认SQL Server (MSSQLSERVER)或SQL Server (SQLEXPRESS)服务状态为「正在运行」在 SQL Server Management Studio (SSMS) 中用 Windows 账户登录新建数据库ProjectManagerDB。回到 VS在Web.config文件中找到connectionStrings节点替换为以下内容关键参数已加注释connectionStrings !-- server.\\SQLEXPRESS本地命名实例若用默认实例则写 server. databaseProjectManagerDB刚创建的数据库名 integrated securitytrue启用 Windows 身份验证无需用户名密码 MultipleActiveResultSetstrue允许同一连接上并发执行多个查询Web Forms 页面常需嵌套数据绑定 -- add nameProjectManagerConnectionString connectionStringserver.\SQLEXPRESS;databaseProjectManagerDB;integrated securitytrue;MultipleActiveResultSetstrue; providerNameSystem.Data.SqlClient / /connectionStrings注意providerNameSystem.Data.SqlClient是 .NET Framework 下的标准驱动。若你误用了Microsoft.Data.SqlClient.NET Core 驱动会在运行时报Could not load type System.Data.SqlClient.SqlConnection—— 这是新手最常翻车的点之一。2.3 创建第一个数据访问层用 ADO.NET 手写 SqlCommand 验证连接通路在App_Code文件夹下新建类DbHelper.cs写入最简数据库连通性测试代码using System; using System.Data.SqlClient; public class DbHelper { private static string connectionString System.Configuration.ConfigurationManager .ConnectionStrings[ProjectManagerConnectionString].ConnectionString; public static bool TestConnection() { try { using (SqlConnection conn new SqlConnection(connectionString)) { conn.Open(); // 关键只开连接不执行查询最快验证 return conn.State System.Data.ConnectionState.Open; } } catch (SqlException ex) { // 记录具体错误码比泛泛的 连接失败 有用得多 System.Diagnostics.Debug.WriteLine($SQL Error {ex.Number}: {ex.Message}); return false; } } }然后在Default.aspx.cs的Page_Load中调用protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { bool isConnected DbHelper.TestConnection(); Response.Write($数据库连接状态strong{(isConnected ? ✅ 成功 : ❌ 失败)}/strong); } }运行项目CtrlF5页面显示 ✅ 成功说明 VS、SQL Server、连接字符串三者已打通。这是后续所有数据操作的地基地基不牢EF 生成的 DbContext 也会报Login failed for user。3. 用 Entity Framework 6.4 Code First 建模从 C# 类自动生成 SQL Server 表结构3.1 安装 Entity Framework 6.4 并初始化 DbContext在解决方案资源管理器中右键项目 → 「管理 NuGet 包」→ 切换到「联机」选项卡 → 搜索EntityFramework→ 选择6.4.4.NET Framework 最终稳定版兼容性最好避免用 6.5 可能引发的System.Data.Entity命名空间冲突→ 安装。安装完成后在App_Code下新建Models文件夹添加类Project.csusing System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace ProjectManagerWeb.Models { [Table(Projects)] // 显式指定表名避免 EF 默认复数化Projects vs Project public class Project { [Key] public int Id { get; set; } [Required] [StringLength(100)] public string Name { get; set; } [StringLength(500)] public string Description { get; set; } public DateTime StartDate { get; set; } public DateTime? EndDate { get; set; } // ? 表示可空对应 SQL Server 的 datetime NULL public bool IsActive { get; set; } true; } }同理添加Task.cs注意外键关联using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace ProjectManagerWeb.Models { [Table(Tasks)] public class Task { [Key] public int Id { get; set; } [Required] [StringLength(200)] public string Title { get; set; } public string Description { get; set; } // 外键字段ProjectId 对应 Projects.Id [ForeignKey(Project)] public int ProjectId { get; set; } // 导航属性EF 会自动加载关联的 Project 对象 public virtual Project Project { get; set; } public DateTime DueDate { get; set; } public int Status { get; set; } // 0未开始, 1进行中, 2已完成 } }接着创建ProjectManagerContext.csusing System.Data.Entity; using ProjectManagerWeb.Models; namespace ProjectManagerWeb.Models { public class ProjectManagerContext : DbContext { // 构造函数传入 Web.config 中定义的连接字符串名称 public ProjectManagerContext() : base(ProjectManagerConnectionString) { // 关闭数据库初始化策略避免首次运行时自动删库重建生产环境绝对禁用 Database.SetInitializerProjectManagerContext(null); } public DbSetProject Projects { get; set; } public DbSetTask Tasks { get; set; } // 可选重写 OnModelCreating 配置关系此处一对多已通过 ForeignKey 属性定义无需额外代码 protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); } } }3.2 执行 Package Manager Console 命令生成迁移脚本并更新数据库打开 VS 的「工具」→ 「NuGet 包管理器」→ 「程序包管理器控制台」确保「默认项目」下拉框选中你的 Web 项目ProjectManagerWeb然后依次执行# 初始化迁移生成第一个迁移文件含建表语句 PM Enable-Migrations -ContextTypeName ProjectManagerWeb.Models.ProjectManagerContext # 创建迁移快照Migration 名称可自定义如 InitialCreate PM Add-Migration InitialCreate # 将迁移应用到数据库真正执行 CREATE TABLE PM Update-Database执行成功后回到 SSMS刷新ProjectManagerDB数据库你会看到Projects和Tasks两张表已自动创建字段类型、主键、外键约束、NULL/NOT NULL 规则均与 C# 类定义严格一致。例如Tasks.ProjectId自动设为外键指向Projects.IdStartDate对应datetime NOT NULL。逻辑说明Enable-Migrations在项目中生成Migrations文件夹和Configuration.csAdd-Migration根据当前DbContext与数据库差异生成.cs迁移文件内含Up()和Down()方法Update-Database执行Up()中的 SQL。整个过程不依赖 designer.cs 或 edmx 文件纯代码驱动版本控制友好。4. 在 ASP.NET Web Forms 页面中实现任务列表的增删改查CRUD4.1 设计 Default.aspx 页面用 GridView ObjectDataSource 绑定数据打开Default.aspx删除默认 HTML粘贴以下标记精简版仅保留核心控件% Page LanguageC# AutoEventWireuptrue CodeBehindDefault.aspx.cs InheritsProjectManagerWeb.Default % !DOCTYPE html html xmlnshttp://www.w3.org/1999/xhtml head runatserver title项目任务管理/title /head body form idform1 runatserver div h2任务列表/h2 !-- GridView展示任务数据 -- asp:GridView IDgvTasks runatserver AutoGenerateColumnsfalse DataKeyNamesId AllowPagingtrue PageSize10 OnRowCommandgvTasks_RowCommand OnRowDeletinggvTasks_RowDeleting Columns asp:BoundField DataFieldTitle HeaderText标题 / asp:BoundField DataFieldProject.Name HeaderText所属项目 / !-- 导航属性绑定 -- asp:BoundField DataFieldDueDate HeaderText截止日期 DataFormatString{0:d} / asp:TemplateField HeaderText状态 ItemTemplate %# Eval(Status).ToString() 0 ? 未开始 : Eval(Status).ToString() 1 ? 进行中 : 已完成 % /ItemTemplate /asp:TemplateField asp:CommandField ShowDeleteButtontrue ButtonTypeLink / /Columns /asp:GridView !-- ObjectDataSource桥接 C# 业务逻辑层与 GridView -- asp:ObjectDataSource IDodsTasks runatserver TypeNameProjectManagerWeb.BLL.TaskService SelectMethodGetAllTasks DeleteMethodDeleteTask / !-- 添加任务表单 -- h3新增任务/h3 table tr td标题/td tdasp:TextBox IDtxtTitle runatserver Width200px //td /tr tr td所属项目/td td asp:DropDownList IDddlProjects runatserver DataTextFieldName DataValueFieldId / /td /tr tr td截止日期/td tdasp:TextBox IDtxtDueDate runatserver TextModeDate //td /tr tr td colspan2asp:Button IDbtnAdd runatserver Text添加 OnClickbtnAdd_Click //td /tr /table /div /form /body /html4.2 编写业务逻辑层BLL分离数据访问与页面逻辑在App_Code下新建BLL文件夹添加TaskService.csusing System; using System.Collections.Generic; using System.Linq; using ProjectManagerWeb.Models; namespace ProjectManagerWeb.BLL { public class TaskService { // 获取所有任务含关联项目名称 public ListTask GetAllTasks() { using (var ctx new ProjectManagerContext()) { // 使用 Include 加载导航属性避免 N1 查询 return ctx.Tasks .Include(t t.Project) // 关键预加载 Project 对象 .OrderBy(t t.DueDate) .ToList(); } } // 删除任务 public void DeleteTask(int taskId) { using (var ctx new ProjectManagerContext()) { var task ctx.Tasks.Find(taskId); if (task ! null) { ctx.Tasks.Remove(task); ctx.SaveChanges(); } } } // 添加新任务 public void AddTask(string title, int projectId, DateTime dueDate) { using (var ctx new ProjectManagerContext()) { var task new Task { Title title, ProjectId projectId, DueDate dueDate, Status 0 // 默认未开始 }; ctx.Tasks.Add(task); ctx.SaveChanges(); } } } }4.3 在 Default.aspx.cs 中处理页面事件绑定下拉框、响应按钮点击using System; using System.Web.UI.WebControls; using ProjectManagerWeb.BLL; using ProjectManagerWeb.Models; public partial class Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { BindProjectsDropdown(); BindTasksGrid(); } } private void BindProjectsDropdown() { using (var ctx new ProjectManagerContext()) { ddlProjects.DataSource ctx.Projects.Where(p p.IsActive).ToList(); ddlProjects.DataBind(); } } private void BindTasksGrid() { var service new TaskService(); gvTasks.DataSource service.GetAllTasks(); gvTasks.DataBind(); } protected void btnAdd_Click(object sender, EventArgs e) { if (!string.IsNullOrWhiteSpace(txtTitle.Text) !string.IsNullOrEmpty(ddlProjects.SelectedValue) DateTime.TryParse(txtDueDate.Text, out DateTime dueDate)) { var service new TaskService(); service.AddTask(txtTitle.Text, int.Parse(ddlProjects.SelectedValue), dueDate); // 清空表单并刷新列表 txtTitle.Text ; txtDueDate.Text ; BindTasksGrid(); } } protected void gvTasks_RowDeleting(object sender, GridViewDeleteEventArgs e) { int taskId (int)gvTasks.DataKeys[e.RowIndex].Value; var service new TaskService(); service.DeleteTask(taskId); BindTasksGrid(); } }运行项目你将看到一个可交互的任务列表下拉框选择项目、输入标题和日期、点击添加列表中显示任务标题、所属项目名、截止日期、状态文字点击「删除」链接即可移除记录。所有数据操作均通过强类型 C# 方法完成编译期就能捕获字段名错误比拼接 SQL 字符串安全十倍。5. 避坑指南ASP.NET SQL Server 项目中最常踩的 5 个深坑及血泪解法5.1 现象GridView 删除后页面不刷新仍显示已删记录原因OnRowDeleting事件中只调用了DeleteTask()但未重新绑定gvTasks.DataSource。Web Forms 的 ViewState 会缓存旧数据DataBind()必须显式调用。解决在gvTasks_RowDeleting方法末尾添加BindTasksGrid();见 4.3 节代码确保删除后立即刷新数据源。5.2 现象ddlProjects下拉框为空或报错Object reference not set to an instance of an object原因BindProjectsDropdown()中ctx.Projects.ToList()返回空集合但DataBind()仍被执行或ProjectManagerContext构造函数中base(...)的连接字符串名称拼写错误如ProjectManagerConnString少了个tion。解决第一步在BindProjectsDropdown()开头加if (ctx.Projects.Any())判断第二步用System.Diagnostics.Debug.WriteLine(connectionString)打印实际连接字符串对照Web.config逐字符核对名称。5.3 现象添加任务时ProjectId总是 0导致外键约束失败报错INSERT statement conflicted with the FOREIGN KEY constraint原因ddlProjects.SelectedValue在Page_Load的!IsPostBack分支外未初始化首次加载时SelectedValue为空字符串int.Parse()抛异常但被静默吞掉未开启CustomErrors或前端 HTML 中runatserver缺失导致服务器端无法识别控件。解决在btnAdd_Click中增加防御性判断!string.IsNullOrEmpty(ddlProjects.SelectedValue)检查DropDownList标签是否含runatserver在Page_Load中对IsPostBack为 true 时也调用BindProjectsDropdown()确保回发时下拉框有值。5.4 现象SQL Server 2019 安装后VS 连接时报A network-related or instance-specific error occurred...原因SQL Server 默认关闭 TCP/IP 协议或 Windows 防火墙阻止了 1433 端口。解决打开 SQL Server Configuration Manager → 展开「SQL Server 网络配置」→ 右键「MSSQLSERVER 的协议」→ 启用「TCP/IP」→ 双击进入属性 → 「IP 地址」选项卡 → 拉到最下方「IPAll」→ 清空「TCP 动态端口」设置「TCP 端口」为1433→ 重启 SQL Server 服务再在 Windows 防火墙「入站规则」中启用「SQL Server (TCP-In)」。5.5 现象Entity Framework 迁移时报错The model backing the context has changed since the database was created原因ProjectManagerContext类或实体类如Project.cs被修改过但未执行Add-Migration生成新迁移直接Update-Database。EF 发现模型与数据库 schema 不一致。解决立即执行Add-Migration FixSchemaMismatch名称自定义→ 查看生成的.cs文件中Up()方法是否包含预期的ALTER TABLE或ADD COLUMN→ 再执行Update-Database。切勿手动修改数据库表结构后再用 EF必须走迁移流程否则下次Add-Migration会生成错误的差异脚本。6. 进阶技巧用 SQL Server 的 computed column C# 属性映射自动计算任务延迟天数并前端高亮6.1 在数据库中添加计算列让 SQL Server 原生计算延迟天数回到 SSMS对Tasks表执行以下 T-SQL无需改 C# 类EF 会自动识别-- 添加 computed column延迟天数负数表示未到期正数表示已逾期 ALTER TABLE Tasks ADD DelayDays AS CASE WHEN DueDate GETDATE() AND Status 2 THEN DATEDIFF(day, DueDate, GETDATE()) ELSE 0 END PERSISTED;说明PERSISTED表示该列物理存储可建索引、参与 WHERE 条件CASE逻辑确保只有「未完成且已过期」的任务才计算正延迟天数避免已完成任务因时间推移而数值变大。6.2 在 Task.cs 中映射计算列作为只读属性暴露给 C#在Task.cs类中添加// 对应数据库 computed columnEF 会自动映射无需 [Column] 特性 public int DelayDays { get; set; }6.3 在 GridView 中条件渲染逾期任务标题标红修改Default.aspx中GridView的Title列为TemplateField加入颜色逻辑asp:TemplateField HeaderText标题 ItemTemplate asp:Label IDlblTitle runatserver Text%# Eval(Title) % ForeColor%# (int)Eval(DelayDays) 0 ? System.Drawing.Color.Red : System.Drawing.Color.Black % / /ItemTemplate /asp:TemplateField同时在TaskService.GetAllTasks()的 LINQ 查询中确保DelayDays被包含默认会因是表列return ctx.Tasks .Include(t t.Project) .OrderBy(t t.DueDate) .ToList(); // DelayDays 已在 SELECT * 中6.4 验证效果用真实数据测试延迟逻辑在 SSMS 中插入测试数据INSERT INTO Projects (Name, StartDate, IsActive) VALUES (ERP升级项目, 2024-01-01, 1); INSERT INTO Tasks (Title, ProjectId, DueDate, Status) VALUES (接口联调, 1, 2024-06-01, 0); -- 应显示延迟天数因今天是 2024-07-01刷新网页你会看到「接口联调」标题变为红色且DelayDays值为30假设今天是 2024-07-01。这个方案的优势在于计算逻辑在数据库层不消耗 ASP.NET 应用服务器 CPU延迟天数随GETDATE()实时变化无需后台定时任务刷新C# 层只需读取无计算负担。我带过的三个制造业项目都用这套computed column EF 映射方式实现了「逾期自动预警」「工时超支标黄」「合同到期前30天提醒」等功能上线后 DBA 只需关注索引碎片开发不用碰时间计算逻辑运维日志里一眼就能看出是 SQL 还是 C# 出的问题。这种把合适的事交给合适的层做的习惯省下的调试时间够你多喝十杯咖啡。希望帮到你。本文还有配套的精品资源点击获取
返回列表