
简介这是一套面向C#开发者与数据库初学者的Access数据库操作源码工程针对项目中反复编写Access连接与增删改查代码的痛点将常用能力封装为独立类库可直接引入任意项目复用省去从零开发的时间。资源包共97个文件以41个dll、13个cs源码、8个json配置及csproj、sln等工程文件为主压缩包约2.53MB结构清晰便于按模块查阅。工程覆盖三种常用连接字符串获取、代码创建Access数据库与数据表、通用SQL执行帮助类含单条与批量插入、事务处理、指定内容查询、分页查询、更新、删除以及获取所有表名与字段名等能力并附带完整的建库建表与增删改查使用示例。目前已有1996人学习下载适合希望快速掌握C#操作Access、减少重复造轮子的开发者参考借鉴。1. 从一次上位机改造说起这套 C# Access 操作源码到底能省多少事去年帮朋友改造一台老设备的配套上位机需求很朴素本地存工艺参数、存报警记录、按时间段查历史数据。数据库选型时纠结了半天SQLite 要带原生 DLL、SQL Server 部署太重最后拍板用 Access——.mdb文件直接扔在程序目录客户拷贝走就能用零安装。结果真动手才发现C# 操作 Access 的坑一点不比 SQLite 少连接字符串有好几种写法、OleDb参数顺序必须和 SQL 里占位符一致、批量插入不开事务慢到怀疑人生、建库还得先判断文件存不存在。这套Test_LiteDbOpc工程就是那会儿翻出来的作者把 Access 的建库、建表、增删查改、分页、事务、元数据读取全部封装成了独立类库AccessDBHelper.cs、AccessDBSqlHelper.cs、MdbHelper.cs各管一摊生成后直接丢进任何 .NET 项目引用即可。它解决的不是什么高深算法而是「每次新项目都要重写一遍 Access 样板代码」这件烦人事。适合做 C# 上位机、工控采集、小型桌面管理系统的同行尤其是那些被 Access 连接字符串和OleDb参数顺序反复折磨过的人。2. 拆开工程看结构三个 Helper 各管什么为什么这么分2.1 从解决方案目录反推模块职责拿到一个源码包我习惯先看目录结构再决定要不要深入。这个工程的顶层很干净Test_LiteDbOpc.sln是入口Test_LiteDbOpc是控制台示例工程LiteDbHelper是真正要复用的类库README.md和.gitignore说明作者有基本的工程习惯。LiteDbHelper目录下四个文件是核心文件职责典型调用场景AccessDBHelper.cs数据库级操作建库、判断库是否存在、获取连接字符串程序启动时初始化AccessDBSqlHelper.csSQL 执行增删改查、批量、事务、分页业务数据读写MdbHelper.cs元数据取所有表名、取表的所有列名动态表结构、通用查询界面Model目录实体类定义强类型映射这种拆法的好处是职责边界清晰。AccessDBHelper只管「库」这个层级AccessDBSqlHelper只管「语句」这个层级MdbHelper只管「结构」这个层级。很多网上抄来的 Access 帮助类把这三件事揉在一个文件里几百行下去自己都找不到方法。作者这么分至少说明是正经项目里磨出来的。2.2 连接字符串的三种写法与选择逻辑Access 连接字符串是第一个劝退点。AccessDBHelper里封装了三种常用形式我按自己的理解还原一下选择逻辑// 形式一老版本 .mdbJet 引擎32 位进程 string connStr1 ProviderMicrosoft.Jet.OLEDB.4.0;Data Source dbPath; // 形式二新版本 .accdbACE 引擎需装 Access Database Engine string connStr2 ProviderMicrosoft.ACE.OLEDB.12.0;Data Source dbPath; // 形式三带密码的 .mdb string connStr3 ProviderMicrosoft.Jet.OLEDB.4.0;Data Source dbPath ;Jet OLEDB:Database Password pwd;逻辑说明.mdb是 Access 2003 及以前的格式用Jet.OLEDB.4.0.accdb是 2007 以后的格式必须用ACE.OLEDB.12.0。参数上Data Source给绝对路径最稳相对路径在服务里跑容易因为工作目录变化而找不到文件。带密码的库在Data Source后追加Jet OLEDB:Database Password注意这是 Jet 引擎的写法ACE 引擎下密码参数名不同。提示如果项目是 64 位编译而机器上只装了 32 位的 Access 驱动运行时会直接抛「未在本地计算机上注册 Microsoft.ACE.OLEDB.12.0」。这不是代码问题是驱动位数问题后面避坑章节细说。2.3 建库与建表代码里怎么落地AccessDBHelper提供了「代码直接创建 Access 数据库」的能力。原理不复杂用ADOX或者CREATE DATABASE语句但 Access 的CREATE DATABASE不能像 SQL Server 那样直接执行得走CatalogClass。常见做法是引用Microsoft.ADOX或者用OleDb连接到一个不存在的路径触发创建。作者封装后调用大概是这样// 创建数据库文件若不存在 AccessDBHelper.CreateDatabase(dbPath); // 创建表传入建表 SQL string createSql CREATE TABLE AlarmLog ( Id AUTOINCREMENT PRIMARY KEY, TagName TEXT(50), AlarmTime DATETIME, AlarmValue DOUBLE, AckFlag BIT ); AccessDBSqlHelper.ExecuteNonQuery(connStr, createSql);逻辑说明CreateDatabase内部先判断文件是否存在不存在才创建避免重复建库报错。建表 SQL 里AUTOINCREMENT是 Access 的自增关键字对应 SQL Server 的IDENTITYTEXT(50)指定长度Access 里不写长度默认 255BIT对应布尔DOUBLE对应双精度。参数上表名和字段名如果和 Access 保留字冲突比如Date、Password要用方括号包起来写成[Date]。2.4 通用 SQL 执行类的接口设计AccessDBSqlHelper是整个类库用得最多的部分。它把单条插入、批量插入、事务、查询、分页、更新、删除都包了。我按常见实现还原几个关键方法签名// 单条插入返回受影响行数 public static int ExecuteNonQuery(string connStr, string sql, params OleDbParameter[] paras) // 批量插入内部开事务 public static int ExecuteBatch(string connStr, string sql, ListOleDbParameter[] paraList) // 查询返回 DataTable public static DataTable ExecuteQuery(string connStr, string sql, params OleDbParameter[] paras) // 分页查询pageIndex 从 1 开始pageSize 每页条数 public static DataTable ExecutePagedQuery(string connStr, string sql, int pageIndex, int pageSize)逻辑说明params OleDbParameter[]这种写法让调用方可以ExecuteNonQuery(conn, sql, p1, p2)直接传参不用手动拼数组。批量插入内部用OleDbTransaction包住循环这是 Access 批量写入的性能关键——不开事务一千条插入可能要几十秒开了事务通常能压到一两秒。分页查询 Access 不支持OFFSET FETCH常见做法是用TOP嵌套子查询先取前 N 页的数据再排除前 N-1 页。参数说明connStr每次传入而不是类内部持有是为了让调用方自己控制连接生命周期避免长连接占用文件锁。OleDbParameter的顺序必须和 SQL 里?占位符出现的顺序严格一致这是OleDb和SqlParameter最大的区别后面避坑章节会重点讲。3. 从零跑通示例建库、建表、增删查改的完整链路3.1 环境准备与工程编译先把工程跑起来再谈复用。环境要求不复杂Visual Studio 2019 或以上.NET Framework 4.5 以上Access 的OleDb在 .NET Core 下支持有限建议用 Framework机器上装好 Access Database Engine。编译步骤# 1. 用 VS 打开 Test_LiteDbOpc.sln # 2. 右键解决方案 - 还原 NuGet 包如果有依赖 # 3. 生成解决方案确认 LiteDbHelper 和 Test_LiteDbOpc 都编译通过 # 4. 把 Test_LiteDbOpc 设为启动项目F5 运行逻辑说明LiteDbHelper是类库编译产物是 DLLTest_LiteDbOpc是控制台引用类库并演示调用。如果编译报「找不到 ADOX 引用」需要在类库项目里手动添加 COM 引用Microsoft ADO Ext. 2.8 for DDL and Security。参数上目标平台建议统一设为 x86 或 x64不要用 AnyCPU因为 Access 驱动是分位数的AnyCPU 在 64 位系统上会以 64 位运行可能和 32 位驱动不匹配。3.2 建库建表的完整调用示例示例工程里Program.cs是入口MdbOPC.cs和AccessDBOpc.cs是演示类。我按典型流程还原一段可抄的调用string dbPath Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Data, demo.mdb); string connStr AccessDBHelper.GetConnectionString(dbPath); // 第一步建库 if (!File.Exists(dbPath)) { AccessDBHelper.CreateDatabase(dbPath); } // 第二步建表 string createTable CREATE TABLE DeviceData ( Id AUTOINCREMENT PRIMARY KEY, DeviceName TEXT(50), CollectTime DATETIME, Value DOUBLE ); AccessDBSqlHelper.ExecuteNonQuery(connStr, createTable); // 第三步插入一条 string insertSql INSERT INTO DeviceData (DeviceName, CollectTime, Value) VALUES (?, ?, ?); AccessDBSqlHelper.ExecuteNonQuery(connStr, insertSql, new OleDbParameter(name, PLC-01), new OleDbParameter(time, DateTime.Now), new OleDbParameter(val, 23.5));逻辑说明GetConnectionString内部根据文件扩展名自动选 Jet 还是 ACE省去调用方判断。建表前先判断表是否存在避免重复执行报错可以用MdbHelper.GetTableNames查一下。插入语句里占位符是?不是nameOleDbParameter的名字其实被忽略真正起作用的是添加顺序这点和 SQL Server 完全不同。参数说明OleDbParameter构造的第一个参数是参数名写什么都行但为了可读性建议和业务对应第二个参数是值类型要匹配DateTime直接传double直接传不要传字符串再让数据库隐式转换Access 的隐式转换规则很迷。3.3 查询、分页与事务的实操查询和分页是业务里用得最多的。示例里演示了指定条件查询和分页查询// 条件查询 string querySql SELECT * FROM DeviceData WHERE CollectTime ? AND DeviceName ?; DataTable dt AccessDBSqlHelper.ExecuteQuery(connStr, querySql, new OleDbParameter(start, DateTime.Today), new OleDbParameter(dev, PLC-01)); // 分页查询第 2 页每页 20 条 DataTable pageDt AccessDBSqlHelper.ExecutePagedQuery(connStr, SELECT * FROM DeviceData ORDER BY CollectTime DESC, 2, 20); // 事务批量插入 var paraList new ListOleDbParameter[](); for (int i 0; i 1000; i) { paraList.Add(new OleDbParameter[] { new OleDbParameter(name, PLC- i), new OleDbParameter(time, DateTime.Now), new OleDbParameter(val, i * 1.0) }); } int affected AccessDBSqlHelper.ExecuteBatch(connStr, INSERT INTO DeviceData (DeviceName, CollectTime, Value) VALUES (?, ?, ?), paraList);逻辑说明条件查询里?的顺序必须和OleDbParameter添加顺序一致第一个?对应start第二个对应dev。分页查询内部用嵌套TOP实现ORDER BY必须写在方法传入的 SQL 里否则分页结果不稳定。批量插入把一千条参数打包成List内部开一个事务循环执行任何一条失败整体回滚。参数说明ExecutePagedQuery的pageIndex从 1 开始不是 0传 0 会返回空。ExecuteBatch的paraList里每个元素的参数顺序要和 SQL 占位符一致长度也要一致否则抛异常。事务的隔离级别默认是ReadCommittedAccess 支持有限不要指望它做复杂的并发控制。3.4 读取所有表名和列名的元数据操作MdbHelper提供的能力在动态场景下很有用比如做一个通用的数据浏览界面或者程序启动时校验表结构// 获取所有表名 Liststring tables MdbHelper.GetTableNames(connStr); // 获取指定表的所有列名 Liststring columns MdbHelper.GetColumnNames(connStr, DeviceData); // 获取列名及类型 DataTable schema MdbHelper.GetTableSchema(connStr, DeviceData);逻辑说明GetTableNames内部查MSysObjects系统表过滤掉MSys开头的系统表只返回用户表。GetColumnNames用OleDbConnection.GetOleDbSchemaTable拿Columns架构信息比查系统表更规范。GetTableSchema返回DataTable包含列名、类型、长度等信息适合做动态表单。参数说明MSysObjects查询需要权限某些情况下 Access 会拒绝访问用GetOleDbSchemaTable更稳。表名区分大小写的情况在 Access 里不常见但列名在某些驱动版本下会返回大写调用方要做兼容。4. 避坑与排查Access 操作里那些让人翻车的细节4.1 现象运行报「未在本地计算机上注册 Microsoft.ACE.OLEDB.12.0」原因这是 Access 操作最高频的报错。本质是驱动位数和进程位数不匹配。机器上装的是 32 位 Access Database Engine而你的程序编译成了 64 位或者反过来。另一个可能是根本没装驱动只装了 Office 但 Office 是即点即用版本不带 OLEDB 驱动。解决先确认机器上装了什么位数的驱动在「控制面板 → 程序和功能」里看Microsoft Access Database Engine。然后确认程序的编译平台在 VS 里把目标平台从AnyCPU改成和驱动一致的x86或x64。如果没装驱动去装对应位数的 Access Database Engine注意 32 位和 64 位不能共存装了 64 位会覆盖 32 位。4.2 现象插入语句参数顺序对了但数据错位原因OleDbParameter是按位置匹配的不是按名字。很多人从 SQL Server 转过来习惯写name、time然后AddWithValue顺序随意结果数据全错位。更隐蔽的是如果 SQL 里同一个?出现多次参数也要重复添加。解决养成习惯OleDbParameter的添加顺序严格对应 SQL 里?从左到右的顺序。参数名可以写但只当注释用。批量插入时每个参数数组的顺序都要一致。排查时把 SQL 和参数列表打日志逐条比对。4.3 现象批量插入一千条要几十秒原因没开事务。Access 每次ExecuteNonQuery都是一次独立提交写文件、刷磁盘一千次就是一千次 IO。这是 Access 作为文件型数据库的固有特性不是代码 bug。解决用ExecuteBatch或者自己手动开OleDbTransaction把循环包在事务里。实测一千条插入从几十秒降到一两秒。注意事务不要开太大一万条以上建议分批提交避免事务日志膨胀和锁表。4.4 现象程序运行中数据库文件被锁无法删除或重命名原因OleDbConnection没关或者DataTable还持有连接。Access 是文件锁连接不释放.ldb锁文件一直在文件就动不了。解决所有OleDbConnection、OleDbCommand、OleDbDataReader都用using包起来确保释放。ExecuteQuery返回DataTable后连接应该已经关了如果内部用的是DataAdapterFill完要手动Dispose。排查时看目录下有没有.ldb文件有就说明还有连接没释放。4.5 现象分页查询结果重复或漏数据原因分页 SQL 的ORDER BY字段有重复值或者排序不稳定。Access 的TOP嵌套分页依赖排序如果排序字段有相同值每次查询返回的顺序可能不同导致分页错乱。解决ORDER BY里加上主键做次级排序保证顺序唯一比如ORDER BY CollectTime DESC, Id DESC。另外确认pageIndex从 1 开始传 0 或负数会返回异常结果。分页查询在数据量大时性能一般Access 不适合做百万级数据的分页超过十万条建议换方案。5. 进阶用法把 Helper 类库抽出来复用到真实项目5.1 类库抽取与项目引用示例工程跑通后真正有价值的是把LiteDbHelper抽出来复用。做法很简单把LiteDbHelper项目整个目录拷到你的解决方案下或者直接编译成 DLL 引用。我一般选前者方便改。引用后你的项目里就能直接调AccessDBHelper、AccessDBSqlHelper、MdbHelper。// 在你自己的项目里 using LiteDbHelper; string connStr AccessDBHelper.GetConnectionString(Data\\app.mdb); DataTable dt AccessDBSqlHelper.ExecuteQuery(connStr, SELECT * FROM Config);逻辑说明GetConnectionString根据扩展名自动选驱动.mdb走 Jet.accdb走 ACE。如果你的库带密码用重载版本传入密码。参数上路径建议用Path.Combine拼绝对路径避免工作目录变化导致找不到文件。5.2 封装成配置驱动的数据访问层真实项目里我习惯在 Helper 之上再包一层把连接字符串放到配置文件把常用查询做成方法public class DataService { private static readonly string ConnStr AccessDBHelper.GetConnectionString( ConfigurationManager.AppSettings[DbPath]); public static DataTable GetAlarms(DateTime start, DateTime end) { string sql SELECT * FROM AlarmLog WHERE AlarmTime BETWEEN ? AND ? ORDER BY AlarmTime DESC; return AccessDBSqlHelper.ExecuteQuery(ConnStr, sql, new OleDbParameter(s, start), new OleDbParameter(e, end)); } public static int InsertBatch(ListAlarmModel list) { var paras list.Select(m new OleDbParameter[] { new OleDbParameter(tag, m.TagName), new OleDbParameter(time, m.AlarmTime), new OleDbParameter(val, m.AlarmValue) }).ToList(); return AccessDBSqlHelper.ExecuteBatch(ConnStr, INSERT INTO AlarmLog (TagName, AlarmTime, AlarmValue) VALUES (?, ?, ?), paras); } }逻辑说明连接字符串从AppSettings读部署时改配置不用重编译。GetAlarms用BETWEEN做时间范围查询注意 Access 的BETWEEN是闭区间。InsertBatch把实体列表转成参数数组列表复用 Helper 的事务批量能力。参数说明ConfigurationManager需要引用System.Configuration。AlarmModel是你自己的实体类字段和表列对应。批量插入时如果列表为空直接返回 0不要传给 Helper避免空事务。5.3 验证清单与我的固定习惯每次把 Access 相关代码交付前我会强制走一遍这个清单检查项验证方法通过标准驱动位数匹配看程序和功能里的驱动看项目目标平台两者一致连接字符串正确手动用 OleDbConnection 打开一次能打开不报错参数顺序一致打日志比对 SQL 占位符和参数列表逐一对齐批量插入有事务插一千条计时秒级完成连接全部释放运行后看目录有无 .ldb无残留分页排序唯一翻到最后一页看有无重复无重复无遗漏这套清单是踩了无数次坑之后固化下来的。有一次交付前没检查驱动位数客户机器上直接报 ACE 未注册远程折腾了一下午。从那以后我每次新建 Access 相关项目第一步就是把目标平台和驱动位数对齐第二步就是跑一遍这个清单。希望帮到你。本文还有配套的精品资源点击获取