首页 / 资讯中心 / 文章详情

C# Winform+SQL Server增删改查样板:连接配置与避坑指南

C# Winform+SQL Server增删改查样板:连接配置与避坑指南 ★ FEATURED ARTICLE
简介面向C#入门及数据库初学者这一份实战型资源包演示了Winform客户端通过SQL Server进行网络连接与本地连接的完整过程并覆盖增删改查的典型实现。项目虽小巧但包含从连接字符串配置、ADO.NET操作到T-SQL语句应用的常见环节适合用来搭建第一个数据库桌面应用。压缩包共28个文件约168KB以C#源文件、项目解决方案、可执行文件及调试信息为主另含SQL查询脚本、资源文件与说明文档源码、脚本与说明相互配套。已有2674人学习/下载受到不少新手关注。通过这份资源可以拿到可运行的CrudTest项目学习如何编写连接串、使用SqlCommand执行增删改查、将数据绑定到界面控件并理解本地与远程SQL Server的配置差异随附的查询语句和配置说明能帮助新手快速验证所学内容整个项目目录清晰适合用Visual Studio打开后边看说明边调试。1. 项目定位一个能让新手少走弯路的 Winform 增删改查样板第一次用 C# 连接 SQL Server最容易翻车的地方不在 C# 代码而在那一行连接字符串明明账号密码都对数据库就在本机程序却报「建立与 SQL Server 的连接时发生网络错误」。这份项目把 C# Winform 与 SQL Server 的增删改查完整链路整理好了——本地连接和网络连接两种方式都写了配置内置建库建表脚本、可直接执行的查询语句和配套窗体代码。拿到之后改库名、改表名几分钟就能跑起来。适合刚接触数据库编程的新手也适合需要在局域网快速交付一个桌面录入工具的一线开发。源码里的配置文件、SQL 脚本、数据访问封装都标了注释照着改就行。2. 先从配置说起技术栈、目录结构和连接字符串拆解2.1 技术选型为什么这套组合适合练手和交付这个项目用的是 C# Winform ADO.NETSystem.Data.SqlClient SQL Server界面展示用 DataGridView 控件。Winform 的优点在于控件拖拽完成布局事件模型直白按钮的 Click 事件、DataGridView 的 CellClick 事件就是数据操作的入口。不需要像 WPF 那样维护绑定关系也不像 Web 后端需要考虑请求管线和依赖注入对「一个窗口 一张表格 几个按钮」的典型管理界面来说Winform 是上手成本最低、交付效率最高的组合。数据访问层没有引入 EF Core 或 Dapper而是直接用 SqlConnection、SqlCommand、SqlDataAdapter。这么选有两个原因一是新手阶段把 ADO.NET 的底层流程走一遍打开连接、构造命令、执行、释放之后再学 ORM 会通透很多二是这套代码没有任何第三方依赖Visual Studio 新建项目就能编译运行拿到源码复现时不需要处理包还原、版本冲突一类的问题。先把最原始的链路跑通后面封装成什么都有底气。2.2 目录结构每个文件的职责先理清拿到源码后先看目录不要一上来就双击 .sln。以下是典型的工程结构具体文件名以源码为准StudentMIS/ ├─ StudentMIS.sln ├─ StudentMIS/ │ ├─ App.config # 连接字符串配置重点改这里 │ ├─ Program.cs # 程序入口Application.Run 启动主窗体 │ ├─ MainForm.cs # 主窗体界面布局 事件处理 │ ├─ MainForm.Designer.cs # 设计器生成的界面代码 │ ├─ DAL/ │ │ └─ DBHelper.cs # 数据访问封装查询与增删改 │ └─ Scripts/ │ ├─ CreateDatabase.sql # 建库建表脚本 种子数据 │ └─ QueryExamples.sql # SELECT / INSERT / UPDATE / DELETE 示例Program.cs 里只有两三行代码作用是启动主窗体基本不用动。MainForm.Designer.cs 是 Visual Studio 设计器自动生成的界面代码不要在文本编辑器里手动改它否则设计器打开会报错。MainForm.cs 存放按钮事件和辅助方法是所有界面逻辑的集中地。DBHelper.cs 是数据访问层把「拿连接、执行 SQL、取结果」封装成方法界面代码只调方法、不直接写 SqlConnection。Scripts 目录下的 SQL 文件是这份资源比较值钱的部分——建库建表、种子数据、增删改查示例全部可执行脱离 C# 也能在 SSMS 里先验证一遍 SQL 效果。2.3 连接字符串拆解本地连接与网络连接就改几个参数App.config 是连接数据库最先要看的地方。一份典型的配置如下?xml version1.0 encodingutf-8 ? configuration connectionStrings !-- 本地连接Data Source 填 . 表示本机默认实例 网络连接Data Source 填 192.168.1.100,1433 表示远程服务器 IP 加端口 -- add nameconnStr connectionStringData Source.;Initial CatalogStudentDB;User IDsa;Password123456;EncryptFalse;TrustServerCertificateTrue providerNameSystem.Data.SqlClient / /connectionStrings /configuration连接字符串里四个关键参数决定了连哪里、连哪个库、用谁的身份参数含义本地连接写法网络连接写法Data Source服务器地址.或localhost或.\SQLEXPRESS192.168.1.100,1433IP逗号端口或服务器名\实例名Initial Catalog数据库名StudentDBStudentDBUser ID登录账号sa或自建账号sa或自建账号Password密码对应账号密码对应账号密码providerName 的作用是告诉 .NET Framework 用哪个数据提供程序解析这个连接串System.Data.SqlClient 对应 SQL Server。注意 connectionStrings 节点必须放在 configuration 节点下并列于 startup 节点放错位置程序启动时会直接抛配置解析异常。新手容易犯两个错误第一网络连接的端口号写成冒号192.168.1.100:1433SQL Server 的客户端组件只认逗号不认冒号这个错会被包装成「客户端无法建立连接」容易被误判成网络不通第二本机如果装的是 SQL Server Express默认实例名是SQLEXPRESS连接字符串只写Server.会找不到实例要写成Server.\SQLEXPRESS。这两个坑在避坑章节会展开讲细节。Encrypt 和 TrustServerCertificate 两个参数是为了应付新版 SQL Server 的加密握手——部分较新的环境连接时若不加会报证书相关错误加上后即可正常通信。3. 核心代码拆解一个增删改查请求从界面到数据库的完整链路3.1 建表脚本先把数据和表结构准备好SQL 脚本是这套资源的第一块基石。用 SSMS 打开并执行 Scripts 目录下的 CreateDatabase.sql会自动建库、建表、写入几条种子数据CREATE DATABASE StudentDB; GO USE StudentDB; GO CREATE TABLE Student ( StudentId INT IDENTITY(1,1) PRIMARY KEY, StudentNo NVARCHAR(20) NOT NULL UNIQUE, StudentName NVARCHAR(20) NOT NULL, Gender NVARCHAR(2) NULL, Birthday DATETIME NULL, ClassName NVARCHAR(50) NULL, Phone NVARCHAR(20) NULL, CreateTime DATETIME DEFAULT (GETDATE()) ); GO INSERT INTO Student (StudentNo, StudentName, Gender, Birthday, ClassName, Phone) VALUES (2024001, N张小明, N男, 2005-03-12, N计算机2401, 13800001111), (2024002, N李小红, N女, 2005-07-09, N计算机2401, 13800002222), (2024003, N王大力, N男, 2004-11-30, N软件2402, 13800003333);代码逻辑说明StudentId 是自增主键IDENTITY(1,1) 让每次插入自动编号C# 端不需要手动赋值StudentNo 加 UNIQUE 约束防止学号重复插入实际项目里这一列建议再建一个唯一索引来支撑按学号查询。NVARCHAR 类型用于存中文比 VARCHAR 稳妥N张小明写法告诉 SQL Server 这是 Unicode 字面量。Birthday 用 DATETIME 类型可以直接绑定到 Winform 的 DateTimePicker 控件省去字符串转换。种子数据的意义是让界面第一次跑起来就有内容能立刻验证 DataGridView 绑定是否正确。3.2 数据访问层DBHelper 的封装与参数化查询数据访问层是这份源码里最值得抄走的部分。下面是精简后的 DBHelper 实现using System; using System.Data; using System.Data.SqlClient; using System.Configuration; namespace StudentMIS.DAL { public class DBHelper { // 从 App.config 读取连接字符串项目需引用 System.Configuration.dll private static readonly string connStr ConfigurationManager.ConnectionStrings[connStr].ConnectionString; /// summary执行查询返回 DataTable/summary public static DataTable GetDataTable(string sql, params SqlParameter[] parameters) { using (SqlConnection conn new SqlConnection(connStr)) using (SqlCommand cmd new SqlCommand(sql, conn)) { if (parameters ! null) { cmd.Parameters.AddRange(parameters); } SqlDataAdapter adapter new SqlDataAdapter(cmd); DataTable dt new DataTable(); adapter.Fill(dt); return dt; } } /// summary执行 新增/修改/删除返回受影响的行数/summary public static int ExecuteNonQuery(string sql, params SqlParameter[] parameters) { using (SqlConnection conn new SqlConnection(connStr)) using (SqlCommand cmd new SqlCommand(sql, conn)) { if (parameters ! null) { cmd.Parameters.AddRange(parameters); } conn.Open(); return cmd.ExecuteNonQuery(); } } } }逻辑说明GetDataTable 方法内部先创建 SqlConnection 和 SqlCommand把外部传入的参数 AddRange 进命令对象最后用 SqlDataAdapter.Fill 填充 DataTable。这里有两个容易忽视的细节。第一Fill 方法会自动管理连接的打开和关闭所以 GetDataTable 里没有显式写 conn.Open()这是 SqlDataAdapter 的行为特性第二ExecuteNonQuery 必须手动 Open因为该方法不会自动打开连接漏掉这一步会直接抛「连接未打开」异常。两个方法都用了 using 块保证无论成功失败连接都会被正确关闭不会耗尽连接池。参数说明SqlParameter 是防 SQL 注入的关键。界面代码如果写string sql SELECT * FROM Student WHERE Name txtName.Text 输入框里填一个 OR 11就能让条件永远成立整表数据被拖走换成 StudentName 占位参数后SqlParameter 把值当作纯数据传输不参与 SQL 语法解析。这是增删改查代码里最有含金量的习惯所有涉及用户输入的 SQL 都应当走参数化。3.3 界面层加载、新增、修改、删除的事件串联界面层的事件写法决定这套代码好不好改。先看加载和查询部分private void MainForm_Load(object sender, EventArgs e) { LoadData(); } private void LoadData() { string sql SELECT StudentId, StudentNo, StudentName, Gender, Birthday, ClassName, Phone FROM Student; DataTable dt DBHelper.GetDataTable(sql); dgvStudents.DataSource dt; } private void btnQuery_Click(object sender, EventArgs e) { // 模糊查询按姓名/学号/班级过滤 string keyword txtKeyword.Text.Trim(); string sql SELECT StudentId, StudentNo, StudentName, Gender, Birthday, ClassName, Phone FROM Student WHERE StudentName LIKE kw OR StudentNo LIKE kw OR ClassName LIKE kw; SqlParameter[] ps { new SqlParameter(kw, % keyword %) }; dgvStudents.DataSource DBHelper.GetDataTable(sql, ps); }逻辑说明LoadData 是主窗体加载后调用的第一个方法把整张表绑定到 DataGridView。DataSource dt赋值后控件会自动按 DataTable 的列名生成列所以列标题默认是英文字段名StudentNo、StudentName。想显示中文标题有两种做法要么改 SQL 用别名SELECT StudentNo AS 学号, StudentName AS 姓名要么在窗体设计器里逐列设置 HeaderText。第二种方式更灵活因为它不影响 SQL 的结果结构。btnQuery 的模糊查询用 LIKE 加前后百分号。注意 kw 里的%是拼接在 C# 侧的SQL 语句里只写LIKE kw这样既防注入又避免在 SQL 里拼字符串。关键字为空时% %等于%%会匹配所有行这个特性能让「清空关键字点查询」退化成查全表刚好满足常见的重置需求。再看新增和修改private void btnAdd_Click(object sender, EventArgs e) { string sql INSERT INTO Student (StudentNo, StudentName, Gender, Birthday, ClassName, Phone) VALUES (StudentNo, StudentName, Gender, Birthday, ClassName, Phone); SqlParameter[] ps new SqlParameter[] { new SqlParameter(StudentNo, txtStudentNo.Text.Trim()), new SqlParameter(StudentName, txtStudentName.Text.Trim()), new SqlParameter(Gender, cboGender.Text), new SqlParameter(Birthday, dtpBirthday.Value), new SqlParameter(ClassName, txtClassName.Text.Trim()), new SqlParameter(Phone, txtPhone.Text.Trim()) }; int rows DBHelper.ExecuteNonQuery(sql, ps); if (rows 0) { LoadData(); MessageBox.Show(新增成功); } else { MessageBox.Show(新增失败请检查必填项); } } private void btnUpdate_Click(object sender, EventArgs e) { if (currentId 0) { MessageBox.Show(请先选中一行要修改的数据); return; } string sql UPDATE Student SET StudentNo StudentNo, StudentName StudentName, Gender Gender, Birthday Birthday, ClassName ClassName, Phone Phone WHERE StudentId StudentId; SqlParameter[] ps new SqlParameter[] { new SqlParameter(StudentId, currentId), new SqlParameter(StudentNo, txtStudentNo.Text.Trim()), new SqlParameter(StudentName, txtStudentName.Text.Trim()), new SqlParameter(Gender, cboGender.Text), new SqlParameter(Birthday, dtpBirthday.Value), new SqlParameter(ClassName, txtClassName.Text.Trim()), new SqlParameter(Phone, txtPhone.Text.Trim()) }; int rows DBHelper.ExecuteNonQuery(sql, ps); if (rows 0) { LoadData(); MessageBox.Show(修改成功); } }逻辑说明新增和修改的 SQL 结构相似区别只在 UPDATE 多了 WHERE 条件。currentId 是窗体级字段在 DataGridView 的 CellClick 事件里赋值——点击某行时把该行的 StudentId 取出来存住修改和删除全靠它定位数据。这是一个值得强调的规范Update 和 Delete 必须带 WHERE且 WHERE 用主键而不是姓名、学号这类业务字段。主键保证唯一性不会出现同名学生被一起误改的情况。新增成功后调用 LoadData() 重新绑定表格这是「数据持久化 界面刷新」的联动。受影响行数 rows 是 ExecuteNonQuery 的返回值SQL 执行成功但没匹配到任何行时返回 0所以 rows 0 不一定是 SQL 写错也可能是数据已被其他窗口删除界面提示语要能兼容这种语义。DataGridView 选中回填的常见做法如下private void dgvStudents_CellClick(object sender, DataGridViewCellEventArgs e) { if (e.RowIndex 0) return; // 点击列头时 e.RowIndex 为 -1直接跳过 DataGridViewRow row dgvStudents.Rows[e.RowIndex]; currentId Convert.ToInt32(row.Cells[StudentId].Value); txtStudentNo.Text row.Cells[StudentNo].Value.ToString(); txtStudentName.Text row.Cells[StudentName].Value.ToString(); cboGender.Text row.Cells[Gender].Value.ToString(); dtpBirthday.Value Convert.ToDateTime(row.Cells[Birthday].Value); txtClassName.Text row.Cells[ClassName].Value.ToString(); txtPhone.Text row.Cells[Phone].Value.ToString(); }逻辑说明CellClick 在点击任意单元格时触发先用 e.RowIndex 是否为 -1 判断点击的是不是列头区域。DataGridViewRow 的 Cells[列名].Value 返回 object赋给文本框前要 ToString()赋给 DateTimePicker 前要 Convert.ToDateTime。如果你的表 Birthday 列允许 NULL这一行 Convert.ToDateTime 会抛异常常见的处理是取值前先判断row.Cells[Birthday].Value DBNull.Value为空就赋默认日期。删除事件相对简单核心仍是用主键定位private void btnDelete_Click(object sender, EventArgs e) { if (currentId 0) { MessageBox.Show(请先选中要删除的数据); return; } DialogResult result MessageBox.Show(确认删除该学生吗, 提示, MessageBoxButtons.YesNo, MessageBoxIcon.Question); if (result ! DialogResult.Yes) return; string sql DELETE FROM Student WHERE StudentId StudentId; SqlParameter[] ps { new SqlParameter(StudentId, currentId) }; int rows DBHelper.ExecuteNonQuery(sql, ps); if (rows 0) { LoadData(); MessageBox.Show(删除成功); } else { MessageBox.Show(删除失败记录可能已不存在); } }删除前弹确认框是桌面程序的习惯做法防止手滑。删除失败时提示「记录可能已不存在」是为了兼容并发场景——两个窗口同时打开另一个窗口先删了这条数据这边再执行 DELETE 受影响行数为 0程序不能崩溃也不能假装成功。4. 避坑指南连接 SQL Server 最容易翻车的五个场景这一章是血泪经验汇总。下面五类问题覆盖了新手连接 SQL Server 时绝大多数报错每一条都是「现象 → 原因 → 解决」的排查思路按顺序排查能省下大量时间。4.1 错误 26/40找不到服务器或服务未启动现象程序启动后点击查询弹出「在建立与服务器的连接时出错。在连接到 SQL Server 时网络相关或特定于实例的错误。找不到或无法访问服务器。请验证实例名称是否正确以及 SQL Server 是否配置为允许远程连接」错误码常见为 26 或 40。原因这个错误大约八成根因不是密码错而是 SQL Server 服务没启动。SqlConnection.Open() 去连接 Data Source 指向的地址时操作系统层面根本没有进程监听自然会报找不到服务器。新手往往先怀疑密码改半天没有效果。解决按 Win R 输入 services.msc 打开服务管理器找到名为 SQL Server (MSSQLSERVER) 的服务确认状态是「正在运行」如果装的是 Express服务名会是 SQL Server (SQLEXPRESS)。把启动类型改成「自动」防止重启电脑后服务不启动改完再运行程序。如果服务已经是运行状态还报 26再去检查 Data Source 的实例名是否匹配。4.2 本地连接与实例名不匹配现象本机确实装了 SQL Server连接字符串写Data Source.;程序仍然报找不到实例。原因Server.或Serverlocalhost指向的是默认实例。如果安装时选了命名实例最常见的是 Express默认实例名 SQLEXPRESS服务注册的名字是localhost\SQLEXPRESS写localhost自然找不到。这是「本地连接 vs 网络连接」里最容易混淆的知识点——本机连接不写实例名跟服务器上跑的到底是默认实例还是命名实例是两件独立的事情。解决确认本机实例名。打开 SSMS 登录窗口服务器名称下拉框里显示的就是可用实例名或者在命令行执行sqllocaldb info查看 LocalDB 列表。拿到实例名后把连接字符串改成Data Source.\SQLEXPRESS或Data Sourcelocalhost\SQLEXPRESS。别人给你连接字符串时如果带了\实例名说明目标服务器上跑的不是默认实例照抄即可。4.3 sa 登录失败错误 18456现象连接字符串里 User ID 写 sa密码确认无误程序仍报「用户 sa 登录失败。用户未与可信 SQL Server 连接关联。」错误码 18456。原因SQL Server 有两种身份验证模式——Windows 身份验证和混合模式SQL Server Windows 身份验证。安装时如果选了 Windows 身份验证模式sa 账号根本不会被启用即使启用密码也可能为空或不符合密码策略。另一个常见原因是密码里包含特殊字符如分号、单引号写在连接字符串里被解析成了配置分隔符。解决用 Windows 身份验证登录 SSMS右键服务器属性 → 安全性 → 选中「SQL Server 和 Windows 身份验证模式」再到「安全性 → 登录名 → sa」右键属性设置一个新密码状态页签确认「启用」最后重启 SQL Server 服务。注意改了身份验证模式后必须重启服务才生效。如果密码含特殊字符考虑用Persist Security InfoTrue配合转义或者干脆把密码改成纯字母数字省心。4.4 网络连接时 TCP/IP 协议未启用现象本地连接一切正常Data Source 写.能连上把连接字符串改成局域网 IP如 192.168.1.100后程序报「无法打开到主机的连接端口 1433 上的服务器拒绝连接」错误码 40。原因SQL Server 默认安装时TCP/IP 协议往往没有启用。本地连接走的是 Shared Memory 共享内存协议不依赖 TCP/IP所以写.一切正常换成 IP 地址后客户端尝试走 TCP 1433 端口而服务器侧 1433 根本没有监听就报无法打开连接。同一个数据库本地能连、IP 不能连十有八九是这里。解决打开 SQL Server Configuration Manager展开「SQL Server 网络配置」→ 找到当前实例 → 右侧协议列表里把 TCP/IP 右键「启用」。再到 TCP/IP 属性 →「IP 地址」页签拉到最底部把 IPAll 的 TCP 端口设为 1433为空则填 1433确定后重启服务。启用后可在命令行执行netstat -an | findstr 1433验证是否有监听。4.5 防火墙拦截 1433 端口现象TCP/IP 协议已启用本机连接正常但从同网段的另一台电脑连接仍然报 40 号错误或连接超时。原因Windows 防火墙默认拦截外部对 1433 端口的访问。TCP/IP 启用后服务器内部开始监听但入站防火墙规则把远端请求挡在了系统之外。本机能连是因为本地回环请求通常不受入站规则限制。解决在 Windows 防火墙的「高级设置」里新建入站规则规则类型选「端口」→ TCP → 特定本地端口填 1433 → 允许连接 → 勾选域/专用/公用三个网络类型 → 命名如 SQLServerPort1433。改完防火墙后从另一台机器用客户端工具测一次。不推荐直接关闭防火墙来测试正式环境只放行必要端口更稳妥。这五类坑有一个共同规律先查服务再查实例名和身份验证最后查网络协议和防火墙。按这个顺序排查大多数连接问题十分钟内能定位。5. 配置实操从本机到网络连接的切换与多环境管理5.1 三步切换从本地连接迁到网络连接程序在本机用.跑通之后要连局域网里的另一台数据库服务器按下面三步做。第一步确认服务器端三项配置SQL Server 服务正在运行、TCP/IP 协议已启用并监听 1433、防火墙放行了 1433 端口。这三项的检查方法在第 4 章已经讲过这里不再重复。提醒一点服务器端改动后都要重启 SQL Server 服务否则配置不生效这个「改了没重启」的坑几乎人人踩过。第二步改连接字符串。网络连接的 Data Source 有两种写法Data Source192.168.1.100,1433 -- 服务器 IP 端口 Data SourceServerName\InstanceName -- 服务器名 实例名如果目标服务器是默认实例优先用 IP 端口写法IP 不依赖 DNS 解析少一层依赖就少一层风险如果服务器上装了多个 SQL Server 实例只能用第二种种写法但前提是服务器开启了 SQL Browser 服务。我一般优先第一种出问题好排查。第三步验证连通性。跑程序之前先用 Visual Studio 的「服务器资源管理器」做一次最小测试右键「数据连接」→「添加连接」填 IP、端口、账号、密码、数据库名点「测试连接」。这里能连上说明连接字符串和服务器配置都没问题问题一定在程序侧这里也连不上问题在数据库或网络侧去查防火墙和协议。这个步骤能帮你把排查范围砍掉一半不用每次都改代码调试。网络连接还有一个隐形坑连接字符串里的端口号必须用逗号写成192.168.1.100:1433会报「客户端无法建立连接」而不是「找不到服务器」很容易被误判成网络不通看到这个报错先检查是不是冒号写成了原因。5.2 用外部配置文件管理多环境连接字符串开发时连本机交付时连服务器每次改 App.config 不是不行但容易发生「本地跑得好好的打包给别人就打不开」的尴尬。两种常见做法可以解决。第一种是 DEBUG / RELEASE 条件编译。在 App.config 里放两个连接字符串节点代码里用预处理指令选择#if DEBUG private static readonly string connStr ConfigurationManager.ConnectionStrings[connStrLocal].ConnectionString; #else private static readonly string connStr ConfigurationManager.ConnectionStrings[connStrServer].ConnectionString; #endif这样开发调试时编译的是 Debug 版本自动走本地连接要交付时切换 Release 编译自动切到服务器连接。缺点是换机器部署仍然要改配置文件。第二种是把连接字符串放到独立的 .config 文件里。这样部署时只需替换一个外部文件不用重新编译。在 App.config 中把 connectionStrings 节点改成配置源指向connectionStrings configSourceconnectionStrings.config /然后在项目目录下新增 connectionStrings.config 文件connectionStrings add nameconnStr connectionStringData Source.;Initial CatalogStudentDB;User IDsa;Password123456;EncryptFalse;TrustServerCertificateTrue / /connectionStrings注意这个文件里不能出现connectionStrings之外的任何节点configSource 是整体替换而不是合并。部署时把这个文件放到 exe 同目录改 IP、改密码不需要重新发布程序对交付场景来说体验好很多。需要提醒的是configSource 指定的文件默认不会复制到输出目录要在 Visual Studio 里把这个文件的属性设为「如果较新则复制」。5.3 别把敏感配置留在代码里源码为了方便演示连接字符串直接写在 App.config 里密码是明文。作为二次开发至少应该改掉两点一是不要把密码硬编码在 C# 代码中——有新手会把连接字符串直接写在 DBHelper 里连配置文件都不走这样换环境就要改代码重新编译二是正式环境建议用 Windows 身份验证Integrated SecurityTrue或给数据库单独建一个只有读写权限的账号而不是让每个客户端都拿 sa 连接。sa 是数据库实例的超级管理员连接字符串一旦泄露等于把整个实例的控制权交出去。最小权限原则在这里不是空话是对自己和数据负责。6. 二次开发把增删改查样板改造成可用的业务系统6.1 改造一DBHelper 扩展为泛型方法原版 DBHelper 已经能覆盖大部分场景但界面层每次都要 new SqlParameter 数组写多了很啰嗦。低成本改动是增加一个泛型读取方法把 DataTable 转成 ListTpublic static ListT GetListT(string sql, FuncIDataReader, T mapper, params SqlParameter[] parameters) where T : class { ListT list new ListT(); using (SqlConnection conn new SqlConnection(connStr)) using (SqlCommand cmd new SqlCommand(sql, conn)) { if (parameters ! null) cmd.Parameters.AddRange(parameters); conn.Open(); using (SqlDataReader reader cmd.ExecuteReader()) { while (reader.Read()) { list.Add(mapper(reader)); } } } return list; }调用时传入一个映射函数把 IDataReader 的当前行转换成实体对象。改造后界面层不再依赖 DataTable字段名写错时编译器会直接报错比运行时才发现列不存在要舒服得多。6.2 改造二分页查询与模糊搜索、导出当数据量超过几百行直接把整张表绑定到 DataGridView 就不合适了。常见做法是 SQL 层用 OFFSET/FETCH 分页SELECT * FROM Student ORDER BY StudentId OFFSET (pageIndex - 1) * pageSize ROWS FETCH NEXT pageSize ROWS ONLY;pageIndex 从 1 开始pageSize 是每页行数。注意使用 OFFSET/FETCH 必须同时有 ORDER BY否则分页结果不稳定。Winform 端在页面底部加「上一页、下一页」按钮和一个当前页标签翻页时重新执行查询并重新绑定 DataSource。导出功能可以直接复用 DataGridView 当前的数据源DataTable dt (DataTable)dgvStudents.DataSource;遍历行和列写入文件流或调用 NPOI 生成 xlsx。因为数据源就是最后一次查询的结果导出和筛选天然联动——页面上筛什么导出来就是什么。6.3 验证方法每次改动后回归一遍改完以上任何一个方向都建议按这个清单过一遍新增时检查必填项校验是否拦截空学号生日选择是否正确写入修改时选中一行改动重新加载后确认改的是本行而不是全表删除时先取消弹窗确认记录还在再删除一条不存在的记录确认不崩溃查询时测中文、数字、空格边界清空关键字能恢复全量最后断开数据库服务启动一次程序看报错是否友好而不是闪退。这个清单是血泪经验攒出来的。早期改项目我经常只测新增不测修改结果改字段名时把 UPDATE 的 SET 子句写漏了列直到演示现场才发现点修改没反应。从那以后我每次动过 SQL 语句或增删字段都强制走一遍「写入 → 重新查询 → 确认持久化」的闭环这个习惯至少挡住过八十次低级错误。希望这份源码和这篇拆解能帮你在连接配置和增删改查这条路上少踩几个坑把精力留在后面更有意思的业务逻辑上——希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站