
简介这份源码工程面向使用C#进行桌面或业务系统开发、需要与Access数据库打交道的开发者尤其适合希望跳过底层封装、直接复用成熟类库的中初级程序员。工程将Access数据库的创建与操作封装为独立类库涵盖三种常用连接字符串获取、代码直接建库建表、通用SQL执行帮助类单条与批量插入、事务处理、指定内容查询、分页查询、更新、删除以及获取库中所有表名与字段名的能力并附带一套完整的增删查改使用示例。压缩包共97个文件以41个dll、13个cs源码、8个json配置及若干exe、pdb、csproj工程文件为主整体约2.53MB结构清晰生成后可直接引入任意项目使用。目前已有1996人学习下载能帮助开发者省去从零搭建Access操作层的时间把精力集中在业务逻辑上。1. 用 C# 把 Access 数据库操作封装成可复用的源码工程从建库到增删改查的完整落地路径接手一个老工控上位机项目时现场数据还散落在几个.mdb文件里客户又不想上 SQL Server只要求“能用、能查、能导出”。这种场景下用 C# 操作 Access 数据库反而是最省事的方案不用装服务、单文件就能跑、部署到工控机上直接双击。但真动手写坑比想象中多——驱动位数不对、连接字符串写错、并发写入直接锁库。这篇就把我实际做过的 Access 数据库操作源码工程拆开讲清楚怎么建库建表、怎么封装增删改查、参数怎么设、哪些地方最容易翻车。适合正在做 C# 上位机、需要本地轻量存储、或者要维护老 Access 项目的开发者。整套思路围绕一个可复用的源码工程展开不是零散代码片段。2. Access 在 C# 项目里的定位与源码工程结构设计2.1 为什么还在用 Access适用边界与选型理由先说清楚 Access 能干什么、不能干什么避免选错方向。Access 数据库本质是一个文件型数据库.mdb是早期格式.accdb是 2007 之后的格式。它最大的优势是零配置不需要独立服务进程一个文件拷来拷去就能用特别适合单机上位机、小型 MES 采集端、实验室数据记录这类场景。但它的边界也很硬。第一并发能力弱多个进程同时写同一个文件容易锁库报“文件已在使用中”或者“无法更新数据库或对象为只读”。第二单文件大小建议控制在 2GB 以内超过之后性能断崖式下降。第三它依赖 ACE 驱动Microsoft Access Database Engine驱动位数必须和你的 C# 程序位数一致这是新手最容易踩的坑。我一般这样判断如果数据量在几十万行以内、并发写入不超过两三个线程、部署环境是单机或小局域网Access 完全够用。如果要做多客户端高频写入直接换 SQLite 或上服务型数据库别硬扛。2.2 源码工程的目录分层一个能复用的 Access 操作工程不应该把所有代码堆在一个Form1.cs里。我习惯按职责分三层数据访问层、实体层、业务调用层。下面是我常用的目录结构直接照着建就行。AccessDbDemo/ ├── Data/ │ ├── AccessHelper.cs // 连接管理与通用执行 │ ├── AccessDbInitializer.cs // 建库建表 │ └── UserRepository.cs // 具体表的增删改查 ├── Models/ │ └── User.cs // 实体类 ├── Config/ │ └── DbConfig.cs // 连接字符串集中管理 └── Program.cs // 调用入口这样分的好处是换数据库时只动 Data 层实体和业务不用改连接字符串集中在一个地方部署时改一处就行。下面逐个说关键实现。2.3 连接字符串与 ACE 驱动的正确配置连接字符串写错是最高频的翻车点。Access 用 OleDb 连接不是 SqlClient。.accdb和.mdb的 Provider 不一样写混了直接报“未注册的提供程序”。// DbConfig.cs public static class DbConfig { // .accdb 用 Microsoft.ACE.OLEDB.12.0 // .mdb 用 Microsoft.Jet.OLEDB.4.0仅32位 public static string ConnectionString(string dbPath) { return $ProviderMicrosoft.ACE.OLEDB.12.0; Data Source{dbPath}; Persist Security InfoFalse;; } }逻辑说明Provider指定驱动.accdb必须用 ACE.OLEDB.12.0 或 16.0Data Source是数据库文件的绝对路径相对路径在部署后经常找不到建议用AppDomain.CurrentDomain.BaseDirectory拼接。参数上如果数据库设了密码追加Jet OLEDB:Database Password你的密码。注意如果你的 C# 程序编译成 64 位就必须装 64 位的 Access Database Engine编译成 32 位就装 32 位。两者不能混。很多人报“未在本地计算机上注册 Microsoft.ACE.OLEDB.12.0”九成是位数不匹配。3. 建库建表与增删改查的完整代码实现3.1 用 C# 代码自动创建 Access 数据库和表Access 不像 SQLite 那样能直接CREATE DATABASE建库要靠 ADOX 或者调用 ACE 的CREATE_DB。我一般用 ADOX引用Microsoft ADO Ext. 6.0 for DDL and Security或者用 COM 方式。更简单的做法是如果库文件不存在先用 ADOX 建再建表。// AccessDbInitializer.cs using ADOX; public static void CreateDatabaseIfNotExists(string dbPath) { if (File.Exists(dbPath)) return; // ADOX 建库 var catalog new Catalog(); catalog.Create($ProviderMicrosoft.ACE.OLEDB.12.0;Data Source{dbPath};); catalog null; // 建表 using var conn new OleDbConnection(DbConfig.ConnectionString(dbPath)); conn.Open(); string sql CREATE TABLE Users ( Id AUTOINCREMENT PRIMARY KEY, UserName TEXT(50), Age INT, CreatedAt DATETIME ); using var cmd new OleDbCommand(sql, conn); cmd.ExecuteNonQuery(); }逻辑说明ADOX 的Catalog.Create负责生成空库文件之后用标准 OleDb 执行CREATE TABLE。Access 的字段类型和 SQL Server 不同AUTOINCREMENT是自增TEXT(n)是变长字符串INT是整型DATETIME是日期。参数上TEXT不写长度默认 255够用但建议显式写。注意ADOX 在 64 位下有时会报 COM 组件注册问题。如果不想引 ADOX可以预先放一个空.accdb模板文件程序启动时复制一份改名这样最稳也是我在工控现场常用的做法。3.2 封装通用的 ExecuteNonQuery 与 ExecuteScalar增删改查不要每次手写连接打开关闭封装成通用方法。核心是using保证连接释放参数化查询防注入。// AccessHelper.cs public static int ExecuteNonQuery(string sql, params OleDbParameter[] parameters) { using var conn new OleDbConnection(DbConfig.ConnectionString(DbPath)); conn.Open(); using var cmd new OleDbCommand(sql, conn); if (parameters ! null) cmd.Parameters.AddRange(parameters); return cmd.ExecuteNonQuery(); } public static object ExecuteScalar(string sql, params OleDbParameter[] parameters) { using var conn new OleDbConnection(DbConfig.ConnectionString(DbPath)); conn.Open(); using var cmd new OleDbCommand(sql, conn); if (parameters ! null) cmd.Parameters.AddRange(parameters); return cmd.ExecuteScalar(); }逻辑说明ExecuteNonQuery返回受影响行数用于 INSERT/UPDATE/DELETEExecuteScalar返回第一行第一列用于 COUNT 或取单个值。参数用OleDbParameter注意 Access 的参数是位置参数不是命名参数SQL 里用?占位添加顺序必须和?出现顺序一致这点和 SQL Server 的p完全不同。参数说明OleDbParameter的OleDbType建议显式指定比如OleDbType.VarWChar对应文本OleDbType.Integer对应整型避免类型推断出错。3.3 参数化插入与更新的正确写法Access 的参数占位符是?顺序敏感。下面是一个插入示例。public static int AddUser(string name, int age) { string sql INSERT INTO Users (UserName, Age, CreatedAt) VALUES (?, ?, ?); var ps new OleDbParameter[] { new OleDbParameter(?, OleDbType.VarWChar) { Value name }, new OleDbParameter(?, OleDbType.Integer) { Value age }, new OleDbParameter(?, OleDbType.Date) { Value DateTime.Now } }; return AccessHelper.ExecuteNonQuery(sql, ps); }逻辑说明三个?对应三个参数顺序不能乱。OleDbType.Date对应 Access 的 DATETIME。更新类似把 SQL 换成UPDATE Users SET UserName? WHERE Id?参数顺序对应即可。注意不要用字符串拼接写 SQL一是注入风险二是日期和文本格式在不同区域设置下会出错。参数化是唯一稳妥做法。3.4 查询与 DataTable 填充查询用OleDbDataAdapter填充DataTable适合绑定到 DataGridView。public static DataTable QueryUsers() { using var conn new OleDbConnection(DbConfig.ConnectionString(DbPath)); conn.Open(); string sql SELECT Id, UserName, Age, CreatedAt FROM Users ORDER BY Id DESC; using var adapter new OleDbDataAdapter(sql, conn); var dt new DataTable(); adapter.Fill(dt); return dt; }逻辑说明OleDbDataAdapter自动管理连接打开关闭Fill把结果集灌进 DataTable。参数上如果只取前 N 条Access 用SELECT TOP 10 ...不是LIMIT。排序、条件过滤都写在 SQL 里不要在 C# 里循环过滤大数据集。4. 避坑与排查Access 操作中最容易翻车的五个点4.1 报“未注册 Microsoft.ACE.OLEDB.12.0”现象程序一连接就抛异常提示未注册提供程序。原因没装 ACE 驱动或者驱动位数和程序位数不一致。解决去装对应位数的 Access Database Engine32 位程序装 32 位驱动64 位程序装 64 位驱动。装完重启程序。如果现场不能装驱动改用 32 位编译加 Jet 驱动操作.mdb。4.2 多线程写入报“文件已在使用中”现象单线程没问题一开多线程就锁库。原因Access 不支持高并发写OleDb 连接默认独占。解决所有写操作加锁串行化用一个lock对象包住写方法或者把写操作集中到一个后台队列单线程消费。读操作可以并发但写必须排队。4.3 日期格式在不同机器上查询失败现象本机测试通过部署到客户机查不到数据。原因Access 的日期字面量依赖系统区域设置#2024/1/1#在某些区域格式下解析失败。解决一律用参数化传日期不要拼字符串。参数用OleDbType.Date让驱动处理格式。4.4 自增主键取回失败现象插入后想拿到新生成的 Id用SELECT IDENTITY报错。原因Access 不支持IDENTITY语法不同。解决用SELECT IDENTITY不行改用SELECT MAX(Id) FROM Users或者插入后在同一个连接内执行SELECT IDENTITY的 Access 写法是SELECT IDENTITY实际不支持正确做法是用OleDbCommand执行SELECT IDENTITY会失败应改用SELECT MAX(Id)或插入时用事务加SELECT。更稳的是插入后立刻SELECT MAX(Id)但并发下有风险所以写操作串行化后没问题。4.5 数据库文件被占用无法删除或替换现象程序关了文件还是删不掉。原因连接没释放或者有未关闭的 DataReader。解决所有连接用usingDataReader 用完立刻 Close。如果还是占用检查是否有隐藏的 OleDb 连接池可以在连接字符串加OLE DB Services-4禁用池化。5. 进阶技巧把 Access 操作工程做成可切换的数据访问层5.1 用接口抽象数据访问方便日后换库如果项目有可能从 Access 迁到 SQLite 或 SQL Server一开始就把数据访问定义成接口。public interface IUserRepository { int Add(User user); void Update(User user); void Delete(int id); DataTable QueryAll(); }然后AccessUserRepository实现这个接口。业务层只依赖接口换库时新增一个SqliteUserRepository即可调用方不改。这是我在多个上位机项目里验证过的做法前期多写一个接口后期省大量重构时间。5.2 批量插入用事务提速Access 单条插入慢批量插入一定要用事务包起来。public static void BatchInsert(ListUser users) { using var conn new OleDbConnection(DbConfig.ConnectionString(DbPath)); conn.Open(); using var tran conn.BeginTransaction(); using var cmd new OleDbCommand(INSERT INTO Users (UserName, Age) VALUES (?, ?), conn, tran); foreach (var u in users) { cmd.Parameters.Clear(); cmd.Parameters.Add(new OleDbParameter(?, OleDbType.VarWChar) { Value u.Name }); cmd.Parameters.Add(new OleDbParameter(?, OleDbType.Integer) { Value u.Age }); cmd.ExecuteNonQuery(); } tran.Commit(); }逻辑说明事务内复用同一个 Command每次清空参数重新赋值减少连接和命令创建开销。一千条插入从十几秒降到一两秒。参数上BeginTransaction后所有命令必须绑定同一个事务否则事务不生效。5.3 验证方法用最小工程跑通全流程写完不要直接上大项目先建一个控制台工程验证建库、建表、插入一条、查询、更新、删除六步全过再集成。我一般会写一个Program.cs按顺序调用每步打印结果。这样出问题能快速定位是驱动、连接字符串还是 SQL 的问题。最后说个我自己的习惯每次部署到新机器前先写一个三行的探测程序只做一件事——打开连接并执行SELECT 1。这一步过了后面基本不会有大问题。这个习惯帮我省了无数次现场排查的时间。希望帮到你。本文还有配套的精品资源点击获取