C# OleDb与SqlDataAdapter增删改查实战:从老式WinForm代码到可复用DbHelper

发布时间:2026/10/11 16:09:38
C# OleDb与SqlDataAdapter增删改查实战:从老式WinForm代码到可复用DbHelper
简介这份C#增删改查源代码面向ASP.NET初学者与需要快速搭建数据库操作模块的开发者聚焦数据库连接与增删改查四大基本操作的实现。资源以OleDbConnection、OleDbCommand为核心演示了连接Access数据库、执行INSERT、DELETE、UPDATE、SELECT语句以及使用OleDbParameter进行参数化查询的完整写法并给出可复用的数据库操作固定模板帮助读者理解连接字符串配置、参数传递与DataReader读取结果等关键环节。压缩包内共1个doc文档约203KB内容以代码示例与知识点讲解为主结构紧凑便于查阅。目前已有819人学习下载适合作为课堂练习、课程设计或小型管理系统的参考素材也可用于对照排查SQL拼接与参数绑定中的常见问题。1. 从一份老式 C# 增删改查代码说起它到底能解决什么问题如果你手里维护过一个用 OleDb 连 Access 的 WinForm 小工具或者正在接手某公司十年前留下的内部管理系统那你大概率见过这种写法连接字符串硬编码在窗体里SQL 语句直接拼字符串增删改查四个按钮各写一遍。这份 C# 增删改查代码就是这类项目的典型样本——它用 OleDbConnection 连db1.mdb用 OleDbParameter 做参数化覆盖了 insert、delete、update、select 四条基本语句另外还附带了一套 SqlDataAdapter DataSet 的批量更新模板。说白了它不是教你“C# 是什么”而是给你一套能直接抄进项目里跑通的数据库操作骨架。适合谁适合刚接触 WinForm 数据库编程的新手照着复现也适合需要快速给老系统补 CRUD 功能的一线开发者。下面我按“先跑通、再拆解、最后避坑”的顺序把这份代码里真正值得注意的细节一条条拆开。2. 把 OleDb 连接和参数化查询跑通从连接字符串到四条 SQL2.1 连接字符串的构成与 OleDb 的选型理由这份代码用的是ProviderMicrosoft.Jet.OLEDB.4.0;Data Source...;Persist Security InfoFalse。为什么是 Jet 而不是 ACE因为.mdb是 Access 2003 及更早版本的格式Jet 引擎原生支持如果你拿到的文件是.accdb那就得换成Microsoft.ACE.OLEDB.12.0否则会直接抛“未注册提供程序”的异常。Persist Security InfoFalse的作用是连接打开后不再保留密码等敏感信息属于一个低成本的安全习惯。路径拼接用的是Application.StartupPath \\db1.mdb这意味着数据库文件必须放在可执行文件同级目录。调试时StartupPath指向bin\Debug发布后指向安装目录很多人第一次跑报“找不到文件”就是没把 mdb 复制过去。// 常见做法把路径拼接和连接创建封装成一个方法避免每个按钮重复写 private OleDbConnection CreateConnection() { // Application.StartupPath 在调试时是 bin\Debug发布后是 exe 所在目录 string path Application.StartupPath \\db1.mdb; // Jet 引擎对应 .mdb如果是 .accdb 要改成 Microsoft.ACE.OLEDB.12.0 string connStr ProviderMicrosoft.Jet.OLEDB.4.0;Data Source path ;Persist Security InfoFalse; return new OleDbConnection(connStr); }参数说明Provider决定用哪个数据库引擎写错就是“未注册提供程序”Data Source必须是绝对路径或相对于工作目录的路径相对路径在 WinForm 里极易翻车Persist Security Info设为 False 是推荐值。2.2 增删改三条语句的参数化写法原始代码里增删改各写了一遍OleDbParameter逻辑是对的但有个细节容易被忽略OleDb 的参数是按位置匹配的不是按名字匹配。也就是说insert into t1(name,pwd) values (a,b)里a对应第一个?位置b对应第二个。你Add的顺序必须和 SQL 里参数出现的顺序一致否则数据会串位——这是 OleDb 和 SqlClient 最大的行为差异之一。// 增参数按 SQL 中出现的顺序 AddOleDb 按位置匹配而非按名字 string sql insert into t1(name,pwd) values (a,b); OleDbParameter p1 new OleDbParameter(a, textBox1.Text); OleDbParameter p2 new OleDbParameter(b, textBox2.Text); OleDbCommand com new OleDbCommand(sql, con); com.Parameters.Add(p1); com.Parameters.Add(p2); com.ExecuteNonQuery(); // 返回受影响行数增删改都用它删和改的结构完全一样只是 SQL 不同。删是delete from t1 where name a改是update t1 set pwd a where name b。注意改语句里a是新的密码b是定位用的名字Add 顺序不能反。2.3 查询用 ExecuteReader 而不是 ExecuteNonQuery查这条和前三者不同用的是ExecuteReader()返回OleDbDataReader然后if (dr.Read())判断有没有读到行。这里有个血泪经验dr.Read()返回 bool第一次调用把游标移到第一行返回 true 表示有数据如果你写成while(dr.Read())就是遍历所有行写成if就是只判断存在性。登录场景用if是对的但别忘了用完dr.Close()否则连接池会被占住。string sql select * from t2 where Namea and Pwdb; OleDbParameter p1 new OleDbParameter(a, textBox1.Text); OleDbParameter p2 new OleDbParameter(b, textBox2.Text); OleDbCommand com new OleDbCommand(sql, con); com.Parameters.Add(p1); com.Parameters.Add(p2); OleDbDataReader dr com.ExecuteReader(); if (dr.Read()) // 只判断是否存在不遍历 { MessageBox.Show(登录通过); } dr.Close(); // 必须关闭否则连接无法复用提示OleDb 的OleDbDataReader不支持HasRows属性在某些旧版本上的稳定行为用Read()判断是最稳妥的写法。3. SqlDataAdapter DataSet 模板批量增删改的另一种思路3.1 为什么需要 DataSet 这套模板前面那套是“一条一条执行 SQL”适合单条操作。但如果你要在 DataGridView 里编辑多行然后一次性提交逐条拼 SQL 就很痛苦。这份代码里的第二套模板用的是SqlDataAdapterDataSet核心思路是Adapter 把数据从数据库拉到 DataSet你在 DataSet 里改然后 Adapter 根据每行的 RowStateAdded/Deleted/Modified自动生成对应的 INSERT/DELETE/UPDATE 语句调用da.Update(ds)一次性提交。注意这套用的是System.Data.SqlClient连的是 SQL Serverserver.;databasenorthwind;uidsa;pwdsa不是 Access。所以这份资源实际上给了你两套不同数据库的 CRUD 模板别搞混了。3.2 查询填充与 DataGridView 绑定private DataSet ds new DataSet(); private SqlConnection conn null; private SqlDataAdapter da null; // 连接字符串server. 表示本机默认实例northwind 是示例库 private const string DRIVER server.;databasenorthwind;uidsa;pwdsa; private const string sql_select select * from region; private void Form1_Load(object sender, EventArgs e) { conn new SqlConnection(DRIVER); da new SqlDataAdapter(sql_select, conn); // Adapter 持有查询语句和连接 da.Fill(ds, table); // 把结果填充到 ds 里名为 table 的 DataTable this.dataGridView1.DataSource ds.Tables[table].DefaultView; // 绑定到表格 }参数说明da.Fill(ds, table)第二个参数是 DataTable 的名字后面ds.Tables[table]要用同一个名字取。DefaultView是 DataTable 的默认视图绑定后 DataGridView 的编辑会直接反映到 DataSet 里。3.3 InsertCommand、DeleteCommand、UpdateCommand 的参数映射这三个 Command 的写法是这套模板的精髓。以 Insert 为例private bool BtnInsert() { da.InsertCommand conn.CreateCommand(); da.InsertCommand.CommandText insert into region values(id,ption); // 参数映射id 对应 DataTable 里的 regionid 列类型 Int长度 4 da.InsertCommand.Parameters.Add(id, SqlDbType.Int, 4, regionid); da.InsertCommand.Parameters.Add(ption, SqlDbType.VarChar, 10, regiondescription); int count da.Update(ds); // 提交 DataSet 里所有 Added 状态的行 return count 0; }关键在Parameters.Add的第三个参数——列名。它告诉 Adapter这个参数的值从 DataTable 的哪一列取。Delete 和 Update 里多了一个SourceVersion DataRowVersion.Original意思是“用原始值来定位行”因为删除和更新需要知道改之前的主键是什么。private bool BtnDelect() { SqlParameter sp new SqlParameter(); da.DeleteCommand conn.CreateCommand(); da.DeleteCommand.CommandText delete region where regionidid; sp da.DeleteCommand.Parameters.Add(id, SqlDbType.Int, 4, regionid); sp.SourceVersion DataRowVersion.Original; // 用原始值定位防止改过主键后找不到行 ds.Tables[table].Rows[this.dataGridView1.CurrentRow.Index].Delete(); // 标记为 Deleted int count da.Update(ds); // 提交所有 Deleted 状态的行 return count 0; }注意Rows[...].Delete()只是把行标记为 Deleted 状态并没有真正从 DataTable 移除真正删库是da.Update(ds)那一步。如果你调了Delete()但没调Update()界面上行会消失但数据库里还在。4. 避坑与排查OleDb 和 SqlDataAdapter 最容易翻车的五个点4.1 现象报“未在本地计算机上注册 Microsoft.Jet.OLEDB.4.0”原因目标机器是 64 位系统而 Jet 引擎只有 32 位版本。你的项目如果编译成 AnyCPU 并在 64 位下运行就会找不到提供程序。解决把项目的平台目标改成 x86或者在连接字符串里换用Microsoft.ACE.OLEDB.12.0需要装 Access Database Engine。我一般直接在项目属性里锁死 x86省得部署时再折腾。4.2 现象参数化查询结果串位插入的数据和预期对不上原因OleDb 按位置匹配参数你Add的顺序和 SQL 里参数出现的顺序不一致。比如 SQL 是values(a,b)你先 Add 了b再 Adda数据就反了。解决养成习惯——SQL 里参数从左到右怎么写Parameters.Add就按同样顺序加。别依赖参数名字OleDb 不认名字。4.3 现象da.Update(ds)返回 0数据没进库也没报错原因DataTable 里没有处于 Added/Modified/Deleted 状态的行。常见于你直接改了 DataGridView 但没触发 DataSet 的变更追踪或者da.InsertCommand没设置就调了 Update。解决确认da.InsertCommand、da.DeleteCommand、da.UpdateCommand都赋值了确认 DataGridView 的编辑确实写回了 DataSet绑定 DefaultView 时通常没问题但如果你手动替换了 DataSource 就会断链。4.4 现象连接打开后忘记关闭程序跑一会儿就卡死原因OleDbConnection 和 SqlConnection 都是有限资源con.Open()之后没有对应的con.Close()或using包裹连接池耗尽后后续操作全部阻塞。解决所有连接用using包起来或者至少在 finally 里 Close。原始代码里连接是窗体级字段容易忘记关我一般改成每次操作创建新连接用完即关。4.5 现象dr.Read()之后没关 DataReader再执行其他命令报“连接忙”原因OleDbDataReader 在关闭之前会独占连接同一连接上不能执行第二条命令。解决dr.Close()放在if块外面或者用using(OleDbDataReader dr com.ExecuteReader())自动释放。5. 进阶技巧把两套模板合并成一个可复用的数据库操作类原始代码把 CRUD 散落在各个按钮事件里维护起来很痛苦。我一般会抽一个DbHelper类把连接创建、参数化执行、DataSet 填充三件事封装掉。下面是一个精简版同时支持 OleDb 和 SqlClient 两种场景。public class DbHelper { private readonly string _connStr; private readonly bool _isOleDb; // true 用 OleDbfalse 用 SqlClient public DbHelper(string connStr, bool isOleDb) { _connStr connStr; _isOleDb isOleDb; } // 执行增删改返回受影响行数 public int ExecuteNonQuery(string sql, params (string name, object value)[] paras) { using (var conn _isOleDb ? new OleDbConnection(_connStr) : (DbConnection)new SqlConnection(_connStr)) { conn.Open(); using (var cmd conn.CreateCommand()) { cmd.CommandText sql; foreach (var p in paras) { var param cmd.CreateParameter(); param.ParameterName p.name; param.Value p.value ?? DBNull.Value; cmd.Parameters.Add(param); } return cmd.ExecuteNonQuery(); } } } // 查询并填充 DataTable public DataTable Fill(string sql) { using (var conn _isOleDb ? new OleDbConnection(_connStr) : (DbConnection)new SqlConnection(_connStr)) { var dt new DataTable(); var adapter _isOleDb ? (DbDataAdapter)new OleDbDataAdapter(sql, (OleDbConnection)conn) : new SqlDataAdapter(sql, (SqlConnection)conn); adapter.Fill(dt); return dt; } } }调用时就变成一行var helper new DbHelper(connStr, true); // true 表示 OleDb int rows helper.ExecuteNonQuery( insert into t1(name,pwd) values (a,b), (a, textBox1.Text), (b, textBox2.Text) );参数说明params元组把参数名和值一起传内部按顺序 Add天然规避了 OleDb 位置匹配的坑。_isOleDb开关让你同一套代码在 Access 和 SQL Server 之间切换不用改业务逻辑。验证方法很简单先跑一条 insert再用Fill(select * from t1)把数据拉回来绑到 DataGridView看新增的行在不在。如果不在检查ExecuteNonQuery的返回值是不是 0——是 0 就说明 SQL 没匹配到任何行或者参数没传对。从那以后我每次接老项目第一件事就是把散落的连接字符串和 SQL 语句全部收进一个 Helper 类强制走参数化再也没出现过串位和连接泄漏。希望帮到你。本文还有配套的精品资源点击获取