☰
C# Access 操作源码实战:连接字符串、批量插入、事务与分页避坑指南
2026/10/13 0:02:19 网站建设 项目流程

简介:这份源码工程面向使用C#进行桌面或业务系统开发、需要与Access数据库打交道的开发者,尤其适合希望跳过底层封装、直接复用成熟类库的中初级程序员。项目将Access的各类操作独立封装为专门的帮助类库,涵盖连接字符串获取、数据库与数据表创建、通用SQL执行(单条与批量插入、事务处理、指定内容查询、分页查询、更新、删除),以及获取库中所有表名与字段名等能力,可直接引入任意项目使用。压缩包共97个文件,以41个dll、13个cs源码、8个json配置及若干exe、pdb、csproj工程文件为主,整体约2.53MB,结构清晰、便于二次开发。资源内含一个完整的数据库与表创建、增删查改及元数据读取的使用示例,能帮助读者快速理解调用方式与排错思路。目前已有1997人学习下载,适合作为Access数据访问层的现成参考方案。

1. 从一次上位机改造说起:这套 C# Access 操作源码到底能省多少事

去年帮朋友改一套老上位机,数据落盘用的是 Access,现场跑了六年,机器上还装着 32 位 Office。接手时最头疼的不是业务逻辑,而是那堆散落在各个窗体里的OleDbConnection——连接字符串硬编码、DataReader忘了关、批量插入一条一条循环,几千条数据能卡十几秒。后来翻到这个Test_LiteDbOpc工程,结构很朴素,但把 Access 的常见操作全收进了AccessDBHelper、AccessDBSqlHelper、MdbHelper这几个类里,连接字符串、建库建表、增删改查、分页、事务、取表名列名,该有的都有。它解决的不是什么高深问题,就是「别每次开新项目都从头写一遍 Access 那套胶水代码」。适合谁?做 C# 上位机、工控采集、小型管理系统的同行,尤其是还在用 Access 当本地库、又不想上 SQLite 或 SQL Server 的场景。下面我按「怎么用 → 参数怎么设 → 坑在哪」拆一遍。

2. 工程结构与三种连接字符串:先搞清楚 AccessDBHelper 的入口

2.1 目录里到底放了什么

解压Test_LiteDbOpc.zip后,根目录是Test_LiteDbOpc.sln,下面两个工程:主程序Test_LiteDbOpc和类库LiteDBHelper。类库才是重点,AccessDBHelper.cs、AccessDBSqlHelper.cs、MdbHelper.cs三个文件基本覆盖了 Access 操作的全部入口,Model目录放实体,LiteDBHelper.csproj是类库工程文件。主程序里Program.cs和MdbOPC.cs、AccessDBOpc.cs是调用示例,README.md有最简说明。这种「类库 + 示例」的拆法很实用——类库直接生成 DLL 丢进任何项目引用,示例工程留着当抄作业的模板。

2.2 三种连接字符串,别只会写一种

AccessDBHelper里提供了三种常用连接串的获取方式,这是很多人第一次用 Access 会卡住的地方。常见做法是:

// 方式一:带密码的 mdb(Jet 引擎,32 位) string conn1 = AccessDBHelper.GetConnectionString( @"C:\data\demo.mdb", "admin", "123456"); // 方式二:不带密码的 mdb string conn2 = AccessDBHelper.GetConnectionString(@"C:\data\demo.mdb"); // 方式三:accdb(ACE 引擎,需装 AccessDatabaseEngine) string conn3 = AccessDBHelper.GetConnectionString( @"C:\data\demo.accdb", "", "", true);

逻辑说明:前两种走Microsoft.Jet.OLEDB.4.0,只认.mdb,且必须 32 位进程;第三种走Microsoft.ACE.OLEDB.12.0,认.mdb和.accdb,分 32/64 位两套驱动。参数上,路径建议用绝对路径,相对路径在服务或计划任务里会以系统目录为基准,直接翻车。密码为空时不要传null,传空字符串,否则拼接出来的Jet OLEDB:Database Password=后面会带个null字样,报「无法启动应用程序」。

提示:如果你的项目是 AnyCPU 且跑在 64 位系统上,用 Jet 那两种连接串会直接抛「未在本地计算机上注册 Microsoft.Jet.OLEDB.4.0」。要么把目标平台改成 x86,要么换 ACE 驱动。

2.3 建库和建表:代码里直接生成 mdb

MdbHelper提供了用代码创建 Access 数据库文件的能力,不用先手动建个空 mdb。思路是借助ADOX或CREATE DATABASE语句:

// 创建数据库文件 MdbHelper.CreateDatabase(@"C:\data\newdb.mdb"); // 在库里建表 string sql = @"CREATE TABLE DeviceLog ( Id AUTOINCREMENT PRIMARY KEY, DeviceName TEXT(50), TorqueValue DOUBLE, CollectTime DATETIME)"; MdbHelper.ExecuteNonQuery(conn2, sql);

逻辑说明:CreateDatabase内部走的是CatalogClass.Create,所以项目要引用ADODB和ADOX两个 COM 组件。建表语句里 Access 的类型和 SQL Server 不一样——自增用AUTOINCREMENT不是IDENTITY,文本必须带长度TEXT(50),日期是DATETIME不是DATETIME2。参数上,表名和列名如果带空格或中文,要用方括号[设备日志]包起来,否则解析报错。这一步做完,一个可用的库和表就齐了,后面所有增删改查都基于它。

3. 通用 SQL 执行帮助类:插入、批量、事务、分页怎么写

3.1 单条插入与参数化,别拼字符串

AccessDBSqlHelper是日常用得最多的类。单条插入的常见写法:

string sql = "INSERT INTO DeviceLog (DeviceName, TorqueValue, CollectTime) " + "VALUES (@name, @torque, @time)"; var ps = new Dictionary<string, object> { { "@name", "OPC-01" }, { "@torque", 12.35 }, { "@time", DateTime.Now } }; int rows = AccessDBSqlHelper.ExecuteNonQuery(conn2, sql, ps);

逻辑说明:Access 的OleDb不支持命名参数按名字匹配,它是按顺序匹配的,所以字典传参时顺序必须和 SQL 里@占位符出现的顺序一致,这点和 SQL Server 完全不同,是血泪经验。参数值里DateTime直接传对象即可,驱动会处理;但字符串里如果本身含单引号,参数化能自动转义,拼字符串就会注入或报错。返回值是受影响行数,插入成功一般是 1。

3.2 批量插入:事务包起来,别一条一条提交

几千条数据一条条ExecuteNonQuery,每次都是一次磁盘 IO,慢到怀疑人生。正确姿势是用事务 + 复用命令:

using (var conn = new OleDbConnection(conn2)) { conn.Open(); using (var tran = conn.BeginTransaction()) using (var cmd = conn.CreateCommand()) { cmd.Transaction = tran; cmd.CommandText = "INSERT INTO DeviceLog (DeviceName, TorqueValue, CollectTime) " + "VALUES (?, ?, ?)"; var p1 = cmd.Parameters.Add("?", OleDbType.VarChar, 50); var p2 = cmd.Parameters.Add("?", OleDbType.Double); var p3 = cmd.Parameters.Add("?", OleDbType.Date); foreach (var item in list) { p1.Value = item.DeviceName; p2.Value = item.TorqueValue; p3.Value = item.CollectTime; cmd.ExecuteNonQuery(); } tran.Commit(); } }

逻辑说明:这里用的是?位置参数,因为OleDbCommand对命名参数支持很差,批量场景统一用?更稳。Parameters.Add时把类型和长度一次性定好,循环里只改Value,避免每次重建参数对象。事务把 N 次提交合并成 1 次,实测几千条能从十几秒降到一两秒。参数上,OleDbType.Date对应 Access 的DATETIME,VarChar对应TEXT,类型对不上会报「标准表达式中数据类型不匹配」。

3.3 查询、分页与取表结构

指定内容查询和分页查询在AccessDBSqlHelper里都有封装。Access 的分页没有OFFSET/FETCH,常见做法是用TOP嵌套:

// 第 pageIndex 页,每页 pageSize 条 string sql = $"SELECT TOP {pageSize} * FROM DeviceLog " + $"WHERE Id NOT IN (SELECT TOP {(pageIndex - 1) * pageSize} Id " + $"FROM DeviceLog ORDER BY Id) ORDER BY Id"; var dt = AccessDBSqlHelper.ExecuteDataTable(conn2, sql);

逻辑说明:内层NOT IN先排除掉前面页的主键,外层再取TOP pageSize,这是 Access 里最通用的分页写法。参数上,pageIndex从 1 开始,pageSize别设太大,Access 单表数据量建议控制在十万级以内,再大就该换库了。取所有表名和列名用conn.GetOleDbSchemaTable:

var tables = conn.GetOleDbSchemaTable( OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" }); foreach (DataRow r in tables.Rows) Console.WriteLine(r["TABLE_NAME"]); var cols = conn.GetOleDbSchemaTable( OleDbSchemaGuid.Columns, new object[] { null, null, "DeviceLog", null });

逻辑说明:GetOleDbSchemaTable是 OleDb 驱动自带的元数据接口,比SELECT * FROM MSysObjects稳,后者在权限受限时会读不到。Tables的第四个参数传"TABLE"过滤掉系统表和视图,Columns的第三个参数传表名。这一步在动态生成界面或做数据同步时特别有用。

4. 避坑与排查:Access 操作里最容易翻车的五件事

4.1 报「未注册 Microsoft.Jet.OLEDB.4.0」

现象:程序在开发机跑得好好的,部署到现场 64 位系统直接抛异常。原因:Jet 引擎只有 32 位版本,进程是 64 位就找不到。解决:把项目目标平台改成 x86,或者改用 ACE 驱动(Microsoft.ACE.OLEDB.12.0)并安装对应位数的 AccessDatabaseEngine。注意 ACE 的 32 位和 64 位不能共存,装了 64 位再装 32 位会提示已安装。

4.2 批量插入中途报错,前面数据却进去了

现象:几千条插到一半抛异常,查库发现前面几百条已经在。原因:没包事务,每条自动提交。解决:用 3.2 的事务写法,出错时Rollback,保证要么全进要么全不进。另外注意 Access 单事务写入量别太大,超过几万条建议分批提交,否则日志文件膨胀。

4.3 连接没关,mdb 文件被占用删不掉

现象:程序跑完想删测试库,提示「文件正被另一进程使用」。原因:OleDbConnection、OleDbDataReader没释放。解决:全部用using包起来,DataReader读完及时Close。Access 是文件型数据库,连接不释放文件锁一直在。这也是为什么我一般建议连接用完即关,不要学 SQL Server 搞连接池常驻。

4.4 参数顺序错位,数据插串行

现象:明明传了@name和@torque,插进去发现名字字段是数字。原因:OleDb 按位置匹配参数,字典顺序和 SQL 占位符顺序不一致。解决:要么统一用?位置参数,要么严格保证字典插入顺序与 SQL 中@出现顺序一致。这个坑在字段多的时候特别隐蔽,建议字段超过三个就用?。

4.5 中文乱码或日期格式错

现象:插入的中文变成问号,或者日期报「类型不匹配」。原因:连接串没指定字符集,或日期用了字符串拼接。解决:连接串里加Jet OLEDB:Global Partial Bulk Ops=2之类意义不大,关键是日期一律用参数传DateTime对象,别拼'2024-01-01'这种字符串;中文确保库和字段都是 Unicode 兼容,Access 的TEXT默认支持中文,乱码多半是拼接时编码问题。

5. 进阶用法:把类库抽出来复用,以及一个验证习惯

类库LiteDBHelper生成 DLL 后,直接在其他项目里引用即可,但有几个进阶点值得说。第一,连接字符串别写死,用配置文件或Microsoft.Extensions.Configuration读,现场换库不用重新编译。第二,AccessDBHelper里那三种连接串可以再包一层工厂,根据文件扩展名自动选 Jet 还是 ACE:

public static string Build(string path, string pwd = "") { string ext = Path.GetExtension(path).ToLower(); if (ext == ".accdb") return $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={path};" + $"Jet OLEDB:Database Password={pwd};"; return $"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={path};" + $"Jet OLEDB:Database Password={pwd};"; }

逻辑说明:按扩展名分流,.accdb走 ACE,.mdb走 Jet,密码为空时也拼上空串,避免null字样。这样上层调用只传路径,不用关心驱动差异。第三,验证方法:新建一个空 mdb,跑一遍建表 → 批量插 1000 条 → 分页查第 10 页 → 取表名列名 → 删表,全过说明类库在当前环境没问题。我一般还会故意传一个错误参数,看异常信息是否清晰,方便现场排查。

验证项预期结果常见失败原因
建库建表文件生成,表存在ADOX 未引用
批量插 1000 条1~2 秒内完成未用事务
分页第 10 页返回正确条数TOP 嵌套写错
取表名列名列出全部字段权限不足
删表表消失连接未释放

从那以后我每次接 Access 相关的活,都强制先跑一遍上面这张验证表,再动业务代码。这套源码不算精致,但把该踩的坑都替你踩过一遍,拿来当起点比自己从头写省太多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询