简介:这份资源面向C#与数据库开发的入门学习者,围绕Winform桌面程序如何同时实现SQL Server的本地连接与网络连接,并完成增删改查这一核心场景展开。包内共28个文件,以cs源码、resx与resources资源文件、exe可执行程序、pdb调试文件为主,另附sql脚本、sln解决方案、csproj工程文件及说明文档,压缩包约168KB,结构完整可直接运行调试。资源中给出了数据库连接字符串的写法、ADO.NET中SqlCommand与DataAdapter的用法,以及INSERT、DELETE、UPDATE、SELECT四类语句的落地示例,还涉及app.config配置、异常处理与事务管理、DataGridView数据绑定等实用知识点。目前已有2675人学习下载,适合刚接触C# Winform与SQL Server、希望快速跑通一套可复用增删改查模板的新手参考。
1. 从一次“连不上数据库”的翻车说起:这个 Winform 项目到底能干什么
很多新手写 C# Winform 连 SQL Server,代码在自己电脑上跑得好好的,一换机器就报“登录失败”或者“找不到服务器”。问题往往不在 C# 本身,而是连接字符串写死了本地实例名,或者 SQL Server 的远程连接没打开。这个项目就是冲着这类场景来的:它用一套 Winform 界面,同时演示了本地连接和网络连接两种 SQL Server 访问方式,并把增删改查四个操作完整落地。适合刚学完 C# 基础、想找一个能直接跑起来的数据库练手项目的开发者。项目里附了建库建表的 SQL 语句和数据库配置说明,拿到源码后不用自己猜表结构,照着配就能跑。下面按“项目结构 → 两种连接方式 → 增删改查实现 → 避坑 → 二次开发”的顺序拆一遍。
2. 项目结构与技术选型:Winform + ADO.NET 直连 SQL Server
2.1 为什么是 ADO.NET 而不是 Entity Framework
这个项目的定位是“适合新手”,所以数据访问层没有引入 ORM,而是直接用System.Data.SqlClient命名空间下的SqlConnection、SqlCommand、SqlDataAdapter。原因很实际:新手需要看清一条 SQL 从 C# 发到数据库再拿回结果的全过程,ORM 会把这段链路藏起来。项目里每个按钮对应一个独立方法,方法内部就是“打开连接 → 构造命令 → 执行 → 关闭连接”四步,没有多余封装。等你把 ADO.NET 这套跑熟了,再去用 Dapper 或 EF 会顺很多。
技术栈清单如下:
| 层次 | 技术 | 说明 |
|---|---|---|
| 界面 | Winform | 拖拽控件,事件驱动 |
| 数据访问 | ADO.NET | SqlConnection / SqlCommand / SqlDataAdapter |
| 数据库 | SQL Server | 本地实例或局域网实例 |
| 配置 | App.config | 存放连接字符串 |
| 语言 | C# | .NET Framework 项目 |
2.2 目录结构长什么样
项目解压后大致是这样一个结构,不同版本可能略有差异,但核心文件跑不掉:
项目根目录/ ├── 数据库脚本/ │ ├── CreateDatabase.sql -- 建库建表语句 │ └── InitData.sql -- 初始测试数据 ├── 源码/ │ ├── App.config -- 连接字符串配置 │ ├── MainForm.cs -- 主窗体逻辑 │ ├── MainForm.Designer.cs -- 界面控件定义 │ └── DbHelper.cs -- 数据库操作封装类 └── 说明.txt -- 配置步骤DbHelper.cs是核心,把连接字符串读取、连接打开关闭、执行增删改查的方法都放在这里。MainForm.cs只负责调DbHelper的方法并把结果绑到 DataGridView 上。这种分层对新手友好:界面归界面,数据库归数据库,改一处不影响另一处。
2.3 连接字符串放在 App.config 里
连接字符串不写死在代码里,这是项目里一个值得保留的习惯。App.config里加一段:
<configuration> <connectionStrings> <!-- 本地连接:Data Source 用 . 或 (local) --> <add name="LocalConn" connectionString="Data Source=.;Initial Catalog=TestDB;Integrated Security=True" providerName="System.Data.SqlClient"/> <!-- 网络连接:Data Source 换成局域网 IP --> <add name="RemoteConn" connectionString="Data Source=192.168.1.100;Initial Catalog=TestDB;User ID=sa;Password=你的密码" providerName="System.Data.SqlClient"/> </connectionStrings> </configuration>Data Source=.表示本机默认实例,Integrated Security=True表示用 Windows 身份验证,不用输账号密码。网络连接那条把Data Source换成目标机器的 IP,并且用 SQL Server 账号验证。改连接方式时只动配置文件,不用重新编译代码。
3. 两种连接方式落地:本地连接和网络连接的代码差异
3.1 本地连接:Windows 身份验证怎么配
本地连接的前提是 SQL Server 装在本机,并且当前 Windows 用户有访问权限。代码里读取配置并打开连接:
// 从 App.config 读取本地连接字符串 string connStr = ConfigurationManager .ConnectionStrings["LocalConn"].ConnectionString; using (SqlConnection conn = new SqlConnection(connStr)) { try { conn.Open(); // 打开连接 // 连接成功后可以执行查询 MessageBox.Show("本地连接成功"); } catch (SqlException ex) { // 常见错误:登录失败、找不到实例 MessageBox.Show("连接失败:" + ex.Message); } }ConfigurationManager需要引用System.Configuration程序集。using语句保证连接用完自动释放,不用手动调Close()。如果这里报“无法打开登录所请求的数据库”,先去 SSMS 里确认TestDB存在,并且当前 Windows 账号在 SQL Server 的登录名列表里。
3.2 网络连接:远程访问要过的三道关
网络连接比本地多三个配置点,缺一个都连不上:
第一,SQL Server 要开启 TCP/IP 协议。在 SQL Server 配置管理器里找到“SQL Server 网络配置 → 实例的协议”,把 TCP/IP 状态改为“已启用”,然后重启 SQL Server 服务。
第二,防火墙要放行 1433 端口。默认实例监听 1433,如果改过端口,连接字符串里要加,端口号,比如Data Source=192.168.1.100,1433。
第三,SQL Server 要允许 SQL 账号登录。在 SSMS 里把服务器属性 → 安全性 → 服务器身份验证改为“SQL Server 和 Windows 身份验证模式”,并确保sa账号已启用。
// 网络连接:用 SQL 账号验证 string remoteStr = ConfigurationManager .ConnectionStrings["RemoteConn"].ConnectionString; using (SqlConnection conn = new SqlConnection(remoteStr)) { conn.Open(); // 后续操作和本地连接完全一致 }代码层面本地和网络没有任何区别,差异全在连接字符串和服务器配置上。这也是为什么项目要把两种连接分开演示:让新手明白“连不上”多半不是 C# 代码的问题。
3.3 连接测试按钮的实现
主窗体上一般会放一个“测试连接”按钮,点一下分别测本地和远程:
private void btnTest_Click(object sender, EventArgs e) { // 依次测试两个连接字符串 string[] names = { "LocalConn", "RemoteConn" }; foreach (string name in names) { string cs = ConfigurationManager .ConnectionStrings[name].ConnectionString; using (SqlConnection conn = new SqlConnection(cs)) { try { conn.Open(); MessageBox.Show(name + " 连接成功"); } catch (Exception ex) { MessageBox.Show(name + " 失败:" + ex.Message); } } } }这个按钮的价值在于把“配置对不对”和“增删改查逻辑对不对”分开排查。连接都过不了,后面的查询代码不用看。
4. 增删改查核心实现:从 SQL 语句到 DataGridView 绑定
4.1 查询:SqlDataAdapter 填充 DataTable
查询用SqlDataAdapter最省事,它自动把结果集填进DataTable,再绑到DataGridView:
public DataTable QueryAll() { DataTable dt = new DataTable(); string sql = "SELECT Id, Name, Age, Dept FROM Student"; using (SqlConnection conn = new SqlConnection(connStr)) { // SqlDataAdapter 内部自动打开和关闭连接 SqlDataAdapter adapter = new SqlDataAdapter(sql, conn); adapter.Fill(dt); } return dt; }adapter.Fill(dt)执行查询并把结果写入dt。界面里调用:
dataGridView1.DataSource = dbHelper.QueryAll();DataGridView的列名会自动对应SELECT里的字段名。如果想让列标题显示中文,可以在SELECT里用AS起别名,比如SELECT Name AS 姓名。
4.2 新增:参数化 INSERT 防止注入
新增用SqlCommand的ExecuteNonQuery,返回受影响行数。关键是必须用参数化查询,不要拼字符串:
public int AddStudent(string name, int age, string dept) { string sql = "INSERT INTO Student (Name, Age, Dept) " + "VALUES (@Name, @Age, @Dept)"; using (SqlConnection conn = new SqlConnection(connStr)) { SqlCommand cmd = new SqlCommand(sql, conn); // 参数化:值通过 Parameters 传入,不拼进 SQL cmd.Parameters.AddWithValue("@Name", name); cmd.Parameters.AddWithValue("@Age", age); cmd.Parameters.AddWithValue("@Dept", dept); conn.Open(); return cmd.ExecuteNonQuery(); // 返回插入的行数 } }AddWithValue会自动推断参数类型,简单场景够用。如果字段类型严格,可以用Add("@Age", SqlDbType.Int).Value = age显式指定。返回值大于 0 表示插入成功。
4.3 修改和删除:靠主键定位
修改和删除都要带WHERE条件,通常用主键Id:
public int UpdateStudent(int id, string name, int age, string dept) { string sql = "UPDATE Student SET Name=@Name, Age=@Age, " + "Dept=@Dept WHERE Id=@Id"; using (SqlConnection conn = new SqlConnection(connStr)) { SqlCommand cmd = new SqlCommand(sql, conn); cmd.Parameters.AddWithValue("@Id", id); cmd.Parameters.AddWithValue("@Name", name); cmd.Parameters.AddWithValue("@Age", age); cmd.Parameters.AddWithValue("@Dept", dept); conn.Open(); return cmd.ExecuteNonQuery(); } } public int DeleteStudent(int id) { string sql = "DELETE FROM Student WHERE Id=@Id"; using (SqlConnection conn = new SqlConnection(connStr)) { SqlCommand cmd = new SqlCommand(sql, conn); cmd.Parameters.AddWithValue("@Id", id); conn.Open(); return cmd.ExecuteNonQuery(); } }界面上选中DataGridView某一行,从CurrentRow.Cells["Id"].Value取出主键,再调对应方法。执行完重新查询一次刷新表格,这是最简单的同步方式。
4.4 建库建表 SQL 脚本
项目附带的CreateDatabase.sql大致如下,先建库再建表:
-- 建库 CREATE DATABASE TestDB; GO USE TestDB; GO -- 建表 CREATE TABLE Student ( Id INT IDENTITY(1,1) PRIMARY KEY, -- 自增主键 Name NVARCHAR(50) NOT NULL, Age INT, Dept NVARCHAR(50) ); GO -- 插入测试数据 INSERT INTO Student (Name, Age, Dept) VALUES (N'张三', 20, N'计算机'), (N'李四', 21, N'电子');IDENTITY(1,1)让Id自动增长,插入时不用管。NVARCHAR支持中文,前面加N表示 Unicode 字符串。在 SSMS 里新建查询,把这段贴进去执行即可。
5. 避坑与排查:连接失败、中文乱码、更新不生效
5.1 报“在与 SQL Server 建立连接时出现与网络相关的错误”
现象:本地能连,换台机器或连局域网就报这个。原因通常是 TCP/IP 没启用、防火墙拦了 1433、或者Data Source写的是主机名但解析不到。解决:先在目标机器上用telnet IP 1433测端口通不通,不通就去开协议和防火墙;Data Source直接写 IP,别写计算机名。
5.2 报“用户 ‘sa’ 登录失败”
现象:连接字符串账号密码看着没错,就是登不上。原因多半是sa账号被禁用,或者服务器还是“仅 Windows 身份验证”模式。解决:用 Windows 身份登录 SSMS,服务器属性里改成混合模式,再到安全性 → 登录名里把sa启用并重设密码,最后重启 SQL Server 服务。
5.3 插入中文变成问号
现象:INSERT进去的中文在数据库里显示???。原因是字段类型用了VARCHAR而不是NVARCHAR,或者 SQL 字符串前没加N。解决:建表时中文列统一用NVARCHAR,插入语句写成VALUES (N'张三', ...)。
5.4 修改后 DataGridView 没变化
现象:点了修改按钮,数据库确实改了,但表格还是旧数据。原因是DataGridView绑的是之前查询出来的DataTable,不会自动刷新。解决:每次增删改成功后重新调一次QueryAll()并重新赋给DataSource。这是最直接的做法,数据量小的时候完全够用。
5.5 忘记释放连接导致连接池耗尽
现象:程序跑一段时间后报“超时时间已到,但是未能从连接池获取连接”。原因是SqlConnection没关。解决:所有连接都用using包起来,保证Dispose被调用。SqlDataAdapter.Fill内部会自己开关连接,但SqlCommand手动Open的必须手动关,用using最省心。
6. 二次开发:把直连改成分层架构和可配置切换
这个项目跑通之后,最值得做的改造是把数据库操作从窗体里抽出来,做成独立的数据访问层,并且让本地/网络连接可以运行时切换。我一般会这样改:新建一个IDbService接口,定义QueryAll、Add、Update、Delete四个方法,然后写一个SqlServerDbService实现它。窗体只依赖接口,不直接碰SqlConnection。这样以后想换成 MySQL 或者加一个 SQLite 本地缓存,只需要再写一个实现类,窗体代码一行不用动。
连接切换可以做成下拉框,选中后把对应的连接字符串名存到一个静态变量里,DbService每次取连接字符串时读这个变量:
public static class DbConfig { // 当前使用的连接名,默认本地 public static string CurrentConnName = "LocalConn"; } // DbService 内部取连接字符串 string cs = ConfigurationManager .ConnectionStrings[DbConfig.CurrentConnName].ConnectionString;窗体上放一个ComboBox,选项是“本地连接”和“网络连接”,SelectedIndexChanged里改DbConfig.CurrentConnName,然后重新查询刷新表格。这样不用改配置文件也不用重启程序就能切换数据源。
另一个实用改造是给查询加条件过滤。现在QueryAll是全表查,数据一多表格就卡。可以在界面上加一个文本框和一个查询按钮,把SELECT改成带WHERE Name LIKE @Keyword的形式:
public DataTable QueryByName(string keyword) { DataTable dt = new DataTable(); string sql = "SELECT Id, Name, Age, Dept FROM Student " + "WHERE Name LIKE @Keyword"; using (SqlConnection conn = new SqlConnection(connStr)) { SqlDataAdapter adapter = new SqlDataAdapter(sql, conn); // 前后加 % 实现模糊匹配 adapter.SelectCommand.Parameters.AddWithValue( "@Keyword", "%" + keyword + "%"); adapter.Fill(dt); } return dt; }LIKE配合参数化同样能防注入,%在参数值里拼,不要拼进 SQL 文本。这个改动很小,但能让项目从“演示”变成“能用”。
最后说一个我踩过的坑:早期我把连接字符串直接写在DbHelper的常量里,换台机器就要改代码重新编译。后来统一挪到App.config,部署时只改配置文件,省了很多事。从那以后我每次新建 Winform 数据库项目,第一件事就是把连接字符串抽到配置文件,并且本地和远程各留一条,切换只改一个名字。希望这个项目能帮你把 ADO.NET 这套流程跑顺,后面再上 ORM 会轻松很多。
本文还有配套的精品资源,点击获取