C# Winform连接SQL Server实战:从本地到局域网的增删改查与连接配置

发布时间:2026/10/16 3:50:52
C# Winform连接SQL Server实战:从本地到局域网的增删改查与连接配置
简介一份面向C# Winform新手的SQL Server数据库操作完整示例集中演示网络连接与本地连接两种方式下的增删改查实现。压缩包共28个文件包含C#源码.cs、工程与解决方案配置.sln/.csproj、数据库查询脚本.sql、配置说明.txt以及可直接运行的exe程序整体仅168KB轻量且便于逐项对照学习。已有2674人学习下载适合刚接触桌面数据库开发或准备课程设计的读者。通过阅读说明文档与SQL脚本能快速理解连接字符串配置、ADO.NET常用类用法、数据绑定展示等关键环节配合源码和可执行程序可实际运行验证增删改查效果并在此基础上扩展业务逻辑尤其适合作为WinformSQL Server入门参考。1. C# Winform 连 SQL Server本地连得上、网络连不上才是真正的第一课新手拿到这套 C# Winform 增删改查项目最容易出现的状态不是代码跑不通而是数据库连接字符串改来改去、报错换个不停。这个资源的价值在于它把本地连接和网络连接两种模式同时给了出来还附带了建库脚本和查询语句等于把从零到跑通的最小闭环一次备齐。说白了增删改查本身不难难的是环境而环境问题九成集中在连接链路。这套东西适合三类人刚学完 C# 语法想碰真实数据库的学生、公司里被迫维护 Winform 老系统的半路出家开发者、以及想快速搭一个内部工具给同事用的非专职程序员。如果你已经能独立写出 SQL 却还没在 Winform 里成功显示过一条记录那这篇就是写给你看的。2. 先解决连不连得上连接字符串的四种形态与配置细节2.1 把 App.config 里的连接字符串看懂而不是抄走这套代码里所有数据库访问都通过一个统一入口连接字符串单独放在 App.config 的 connectionStrings 节点下。我见过太多人直接复制别人机器上的配置结果拿回家秒报错原因就是连接字符串里的每一个参数都跟运行环境强绑定。先把四种形态列出来后面再讲怎么改。连接形态连接字符串示例适用场景本地 Windows 身份验证Data Source.;Initial CatalogDemoDB;Integrated SecurityTrue本机开发调试本地 SQL 身份验证Data Source.;Initial CatalogDemoDB;User IDsa;Password123456本机模拟服务器环境局域网 SQL 身份验证Data Source192.168.1.100;Initial CatalogDemoDB;User IDsa;Password123456同事电脑连你的机器远程服务器连接Data Source114.x.x.x,1433;Initial CatalogDemoDB;User IDsa;Passwordxxx部署到云服务器后Data Source 里的句点代表本机换成 IP 就是走网络Initial Catalog 指定数据库名Integrated SecurityTrue 表示用当前 Windows 账户登录而 User ID 和 Password 走的是 SQL Server 自己的账号体系。很多人搞不懂为什么本机能连、别人连不上就是因为第一台机器用的 Windows 身份验证而网络连接必须要 SQL 身份验证加账号密码。这套资源里本地和网络两套配置都给全了对照着改就能少踩一半坑。2.2 启动时读取连接字符串配置写在 App.config 里读取则在程序启动入口完成。项目里一般会有个公共类专门管数据库连接比如 DbHelper。启动时先确保配置被正确读取再谈后续操作。// App.config 里的关键节点 connectionStrings add nameSqlConn connectionStringData Source.;Initial CatalogDemoDB;Integrated SecurityTrue providerNameSystem.Data.SqlClient / /connectionStrings// Program.cs 里启动时读取 string connStr ConfigurationManager.ConnectionStrings[SqlConn].ConnectionString; if (string.IsNullOrEmpty(connStr)) { MessageBox.Show(数据库连接字符串未配置请检查 App.config); Application.Exit(); }读取逻辑写在 Main 方法最前面连不上就直接弹窗提示而不是进主界面后再慢慢报错这对新手排障友好得多。ConfigurationManager 是 System.Configuration 命名空间下的类需要项目引用 System.Configuration.dll这一步容易漏。我一般会顺手加一个日志输出把当前读到的连接字符串打印出来但注意别把密码打到生产日志里。连接字符串里如果密码是明文至少保证这个配置文件不会进版本库。2.3 数据访问层的封装思路这套资源里的增删改查不是每个窗体里各写一套而是把连接、执行、查询统一封装成方法。新手最容易犯的错是每个按钮里 new SqlConnection 然后散布一堆重复代码改一个连接方式就得改几十处。封装之后至少有三个好处连接字符串只在配置里改一次、连接对象统一 using 管理、SQL 执行结果统一返回 DataTable 或者影响行数。public static class DbHelper { // 外部传入连接字符串避免类内部硬编码 public static DataTable ExecuteQuery(string sql, string connStr, 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(); try { conn.Open(); adapter.Fill(dt); } catch (SqlException ex) { throw new Exception(SQL 执行失败 ex.Message); } return dt; } } } // 执行增删改返回受影响行数 public static int ExecuteNonQuery(string sql, string connStr, params SqlParameter[] parameters) { using (SqlConnection conn new SqlConnection(connStr)) { using (SqlCommand cmd new SqlCommand(sql, conn)) { if (parameters ! null) cmd.Parameters.AddRange(parameters); try { conn.Open(); return cmd.ExecuteNonQuery(); } catch (SqlException ex) { throw new Exception(SQL 执行失败 ex.Message); } } } } }代码逻辑不复杂查询走 SqlDataAdapter 填 DataTable增删改走 ExecuteNonQuery 拿影响行数。两个方法都接收连接字符串作为参数而不是自己硬编码这是为了支持后面切换本地和网络模式。SqlParameter 数组用 params 传参调用方可以传也可以不传。注意 using 块保证连接即使异常也会释放这是新手最容易忽略的连接没释放会导致连接池爆掉后面表现为“偶尔能连偶尔连不上”非常难受。3. 增删改查的完整落法参数化查询与 DataGridView 绑定3.1 用户表设计与建表脚本这套资源里附带的 SQL 脚本包含建库、建表和基础数据。先看建表语句别急着运行逐行读一遍再执行。-- 建库 IF DB_ID(DemoDB) IS NULL CREATE DATABASE DemoDB; GO USE DemoDB; GO -- 建用户表 IF OBJECT_ID(Users, U) IS NULL BEGIN CREATE TABLE Users ( Id INT IDENTITY(1,1) PRIMARY KEY, UserName NVARCHAR(50) NOT NULL, Age INT NULL, Email NVARCHAR(100) NULL, CreateTime DATETIME DEFAULT GETDATE() ); END GO -- 初始化两条数据便于后面验证查询 INSERT INTO Users (UserName, Age, Email) VALUES (张三, 25, zhangsanexample.com), (李四, 30, lisiexample.com); GOIDENTITY(1,1) 自增主键不需要手动赋值增删改查里 Insert 语句不写 Id 列就行。UserName 用 NVARCHAR 是因为要考虑中文VARCHAR 在某些字符集下会乱码。CreateTime 给了默认值插入时不传也能自动生成。这个表结构简单到不能再简单但对新手来说恰好合适字段类型涵盖了字符串、整数、日期三种最常用类型后续扩展也方便。3.2 增删改四个方法的完整模板有了 DbHelper 之后具体窗体内的操作就变得很规矩拼 SQL、传参数、执行、刷新列表。很多入门教程喜欢拼字符串比如 string sql INSERT INTO Users VALUES( textBox.Text )这种写法在遇到英文单引号时会直接炸而且存在注入风险。这套代码用的参数化是应该从一开始就养成的习惯。// 新增 private void btnAdd_Click(object sender, EventArgs e) { string sql INSERT INTO Users (UserName, Age, Email) VALUES (Name, Age, Email); SqlParameter[] paras { new SqlParameter(Name, SqlDbType.NVarChar) { Value txtName.Text.Trim() }, new SqlParameter(Age, SqlDbType.Int) { Value Convert.ToInt32(txtAge.Text.Trim()) }, new SqlParameter(Email, SqlDbType.NVarChar) { Value txtEmail.Text.Trim() } }; int rows DbHelper.ExecuteNonQuery(sql, connStr, paras); if (rows 0) { MessageBox.Show(添加成功); LoadData(); } }// 修改按选中行的 Id 更新 private void btnUpdate_Click(object sender, EventArgs e) { if (dataGridView1.CurrentRow null) return; int id Convert.ToInt32(dataGridView1.CurrentRow.Cells[Id].Value); string sql UPDATE Users SET UserNameName, AgeAge, EmailEmail WHERE IdId; SqlParameter[] paras { new SqlParameter(Name, SqlDbType.NVarChar) { Value txtName.Text.Trim() }, new SqlParameter(Age, SqlDbType.Int) { Value Convert.ToInt32(txtAge.Text.Trim()) }, new SqlParameter(Email, SqlDbType.NVarChar) { Value txtEmail.Text.Trim() }, new SqlParameter(Id, SqlDbType.Int) { Value id } }; DbHelper.ExecuteNonQuery(sql, connStr, paras); MessageBox.Show(修改成功); LoadData(); }// 删除先取选中行的主键 private void btnDelete_Click(object sender, EventArgs e) { if (dataGridView1.CurrentRow null) return; int id Convert.ToInt32(dataGridView1.CurrentRow.Cells[Id].Value); if (MessageBox.Show(确认删除该记录, 提示, MessageBoxButtons.YesNo) DialogResult.Yes) { string sql DELETE FROM Users WHERE IdId; SqlParameter[] paras { new SqlParameter(Id, SqlDbType.Int) { Value id } }; DbHelper.ExecuteNonQuery(sql, connStr, paras); LoadData(); } }// 查询全部并绑定到 DataGridView private void LoadData() { string sql SELECT Id, UserName, Age, Email, CreateTime FROM Users ORDER BY Id DESC; DataTable dt DbHelper.ExecuteQuery(sql, connStr); dataGridView1.DataSource dt; }四个操作从代码形态上看差异很小区别只在 SQL 语句本身。新增和修改都用了 占位符参数类型和长度尽量跟数据库字段对齐NVarChar 类型在中文环境下是必须的。Convert.ToInt32 解析文本框内容时如果输入非数字会抛异常我通常会加 int.TryParse 做校验不然用户手滑输入字母程序直接崩掉。删除前弹确认框是个好习惯免得误删后找不到后悔药。每次增删改成功后都调用 LoadData 刷新列表这样界面上看到的永远是最新数据。3.3 查询条件拼接的正确姿势列表查全部简单但实际场景里肯定要按条件筛。这套资源里应该也有一个查询按钮我补一个常见的模糊查询写法注意看参数怎么配合 LIKE。private void btnSearch_Click(object sender, EventArgs e) { string sql SELECT Id, UserName, Age, Email, CreateTime FROM Users WHERE 11; ListSqlParameter paras new ListSqlParameter(); if (!string.IsNullOrWhiteSpace(txtSearchName.Text)) { sql AND UserName LIKE Name; paras.Add(new SqlParameter(Name, SqlDbType.NVarChar) { Value % txtSearchName.Text.Trim() % }); } if (!string.IsNullOrWhiteSpace(txtMinAge.Text)) { sql AND Age MinAge; paras.Add(new SqlParameter(MinAge, SqlDbType.Int) { Value Convert.ToInt32(txtMinAge.Text) }); } DataTable dt DbHelper.ExecuteQuery(sql, connStr, paras.ToArray()); dataGridView1.DataSource dt; }WHERE 11 不是凑数它的作用是让后面每个条件都能直接用 AND 拼接不用费劲判断“这个条件是不是第一个”。这是新手写动态 SQL 最容易出错的地方第一个条件到底用 WHERE 还是 AND拼接边界特别容易出问题。参数列表用 List 动态收集最后转数组传给 DbHelper。LIKE 通配符写在参数值里而不是 SQL 里不要写成 LIKE Name%那会被当成普通字符串。Age 大于等于的查询给个示例说明参数化不只适用于等值匹配范围查询同样有效。4. 从本机搬到局域网网络连接与防火墙的两个拦路虎4.1 SQL Server 配置管理器里要开的三个开关很多人的代码写得好好的换个机器就连不上报错信息翻来覆去就那几句找不到服务器、连接超时、用户登录失败。排查顺序有讲究先确认 SQL Server 服务在跑再确认协议里 TCP/IP 已启用最后确认端口。这套资源里网络连接的坑集中在 SQL Server 本身的默认配置上新装数据库 TCP/IP 默认可能是禁用的走网线必然失败。打开 SQL Server 配置管理器按下面三步操作找到“SQL Server 网络配置”下的“MSSQLSERVER 的协议”右键 TCP/IP 选择启用右键 TCP/IP → 属性 → IP 地址页签拉到最下面 IPAll把 TCP 端口改为 1433服务管理里重启 SQL Server 服务让配置生效改完这些本机用 IP 连接测试一下比如 SqlCmd 或者直接在代码里把 Data Source 改成 127.0.0.1能通再让同事试。TCP/IP 启用后端口要不要改默认的 1433我建议新手先用默认值跑通了再考虑改端口这种加固操作。改完端口忘了重启服务配置不生效这种低级错误我踩过不止一次。4.2 防火墙入站规则最常见的网络连接失败原因SQL Server 配置管理器改完之后同局域网别人连你报错大概率是连接超时。这个问题的头号原因就是 Windows 防火墙把 1433 端口拦了。之前的代码和数据库没有任何问题纯粹是网络层不让进属于环境问题。# 以管理员身份运行放行 1433 端口 netsh advfirewall firewall add rule nameSQL Server 1433 dirin actionallow protocolTCP localport1433加一条入站规则允许 TCP 1433 端口加了之后立刻再测。Windows 防火墙有域、专用、公用三套配置开发机通常处于专用网络测试时如果还不行就检查当前网络配置文件是不是公用公用网络的防火墙策略往往更严。有些公司电脑还有安全软件自带的防火墙需要在软件里也放行一次。做完这些代码不需要改一行网络连接就通了。对于文件资源和文档资源需要用表格、步骤、参数描述来替代代码块。比如这套资源附带的 SQL Server 配置说明可以整理成下表配置项默认值需要改成的值配置路径TCP/IP 协议已禁用已启用SQL Server 配置管理器 → SQL Server 网络配置 → MSSQLSERVER 的协议TCP 端口空1433TCP/IP 属性 → IP 地址 → IPAll → TCP 端口SQL Server 服务运行中重启SQL Server 配置管理器 → SQL Server 服务4.3 账号登录模式的切换SQL Server 身份验证必须打开网络连接还有一个前置条件数据库要允许 SQL 账号密码登录。默认安装时 Windows 身份验证模式通常能用但网络连接必须用 SQL 身份验证。这个设置在 SSMS 里右键服务器实例 → 属性 → 安全性 → 服务器身份验证选择“SQL Server 和 Windows 身份验证模式”然后重启服务。同时还要检查 sa 账号的状态和密码策略。sa 账号如果在安装时选了 Windows 身份验证模式默认是禁用的需要手动启用并设密码。密码强度不够时有些版本会因为策略限制拒绝登录报错提示“不能满足密码策略要求”这时需要把密码设得复杂一些。这一步做完配合上面的 TCP/IP 启用和防火墙放行局域网连接才真正具备条件。5. 常见问题排查五个高频报错的现象、原因与解法5.1 报错“在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误”现象程序启动后查询数据弹窗提示找不到服务器或实例错误描述里包含 network-related 或 instance-specific 字样。原因这个报错是网络类问题的总入口九成是连接字符串的 Data Source 写错或者目标端口连不通。本机用句点没事换到网络模式写 IP 时没带端口号连接就超时还有一种情况是 SQL Server 服务根本没启动。解决先用 SqlCmd 手动测连接排除程序干扰。命令行执行 sqlcmd -S 192.168.1.100,1433 -U sa -P 密码能进 SQL 提示符说明网络通、认证通问题在程序配置连不上则依次检查服务状态、TCP IP 协议、防火墙规则。5.2 报错“用户 sa 登录失败”现象提示用户 sa 登录失败但账号密码明明输对了。原因sa 账号没有启用或者 SQL Server 处于仅 Windows 身份验证模式直接把 SQL 账密认证拒绝了。解决先用 Windows 身份验证方式登录 SSMS在安全性 → 登录名 → sa 上右键属性状态页签里启用登录然后服务器属性里把身份验证模式切成混合模式。改完必须重启 SQL Server 服务不重启不生效这是翻车重灾区。5.3 报错“已成功与服务器建立连接但是在登录过程中发生错误”现象能连上服务器但在登录过程中报错错误代码类似 18456。原因密码错误是最常见原因其次是对应账号被锁定或者映射的数据库不存在。18456 这个错误码对应很多子情况SQL Server 日志里会给出具体错误号比如 18456 后面跟 18470 表示账号被禁用。解决打开 SSMS 看 SQL Server 日志里详细错误号但更快的办法是直接在 SSMS 里用目标账号试登录一次。如果 SSMS 能进程序里多半是连接字符串的密码或者数据库名不匹配如果 SSMS 都进不去回到账号启用与密码重置那一环重新处理。5.4 报错“连接超时”或“超时时间已到”现象本地秒连局域网连接要等 30 秒左右才弹出超时报错。原因TCP 连接没有被目标机器接受防火墙是最主要因素其次是端口没监听。SQL Server 默认动态端口会变化如果没手动固定 1433重启服务后端口可能漂移防火墙规则对着旧端口就失效了。解决先确认 SQL Server 是否在监听 1433。命令行执行 netstat -ano | findstr 1433没有任何输出说明端口没监听回到配置管理器把 IPAll 的 TCP 端口设成 1433 并重启服务有监听但连不上就是防火墙拦截回到 netsh 那条命令。5.5 报错“数据库 DemoDB 不存在”现象连接成功但提示数据库不存在。原因连接字符串里 Initial Catalog 写的库名和实际库名不一致或者 SQL 脚本只在某台机器执行过目标机器上根本没有这个库。解决在 SSMS 里执行 SELECT Name FROM sys.databases 查看当前实例下所有数据库名把 App.config 里 Initial Catalog 改成正确的名字。这套资源的 SQL 脚本要在目标数据库上重新执行一遍别想当然认为库会跟着程序自动过去。6. 再接再厉把 DataGridView 改成翻页查询顺便验证连接方式的切换翻页机制其实不复杂核心是三句话只查当前页需要的数据而不是一次性把所有记录拖到内存里。由于 IDENTITY 自增主键的存在倒序排列取指定范围用 OFFSET FETCH 语法最顺手。这个改造做完你会对整套增删改查的运行链路理解得更透。先封装一个分页查询方法接收页码和每页条数返回当前页数据加上总记录数这样控件上能显示“第 1/5 页共 96 条”。private (DataTable, int) GetPagedData(int pageIndex, int pageSize, string connStr) { string countSql SELECT COUNT(*) FROM Users; int total Convert.ToInt32(DbHelper.ExecuteScalar(countSql, connStr)); string sql SELECT Id, UserName, Age, Email, CreateTime FROM Users ORDER BY Id DESC OFFSET Offset ROWS FETCH NEXT PageSize ROWS ONLY; SqlParameter[] paras { new SqlParameter(Offset, SqlDbType.Int) { Value (pageIndex - 1) * pageSize }, new SqlParameter(PageSize, SqlDbType.Int) { Value pageSize } }; DataTable dt DbHelper.ExecuteQuery(sql, connStr, paras); return (dt, total); }这个写法依赖 SQL Server 2012 以上的 OFFSET FETCH 语法老版本数据库不支持需要改成 ROW_NUMBER() 嵌套查询。参数 Offset 直接算偏移量不需要拼接字符串。翻页按钮和后端逻辑解耦UI 只管传页码数据层永远做同样的事。验证连接方式切换时我一般把 App.config 里的 Data Source 从本地句点改成局域网 IP启动后翻几页能过说明网络连接全程通畅。这个改造虽然简单但能把增删改查、连接字符串、配置修改这几个环节全部串起来相当于一次完整的走查。从那以后我每次接触这类带数据库的 Winform 老项目都会强制走一遍这个流程先看配置、测连接、查账号、翻一页数据全部通过才敢继续往下加功能。希望这套思路对你有帮助少走一步冤枉路都是赚到的。本文还有配套的精品资源点击获取