news 2026/9/18 10:29:08

C#连接Access数据库增删改查实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C#连接Access数据库增删改查实战指南

做桌面小工具绕不开数据库,单机部署场景里“C# + Access”双击就能跟用户数据打交道,不需要装服务端。C#连接ACCESS做增删改查,是课程设计、上位机配置、企业内部小系统里出现频率特别高的一套组合。这篇文章就从零开始,把引用驱动、连接字符串、增删改查代码、常见报错全部过一遍,每一步都给出可以直接抄走的代码。

如果你正卡在“连接不上数据库”“找不到驱动程序”“更新不进去数据”这些坎上,这篇文章能帮你省掉很多翻文档的时间。

1. 动手之前的准备工作

1.1 搞清楚用哪个驱动

Access 数据库文件有两种后缀,老版本是 .mdb,新版本是 .accdb。C# 访问 Access,底层走的是 OLEDB 提供程序,常见的是Microsoft.ACE.OLEDB.12.0。这个驱动需要安装“Microsoft Access Database Engine”,如果没有 Office Access,也要单独装这个引擎。

要注意,ACE 引擎分 32 位和 64 位两个版本。如果你的程序编译成了 x86,就装 32 位驱动;编译成 x64 就装 64 位驱动。最容易翻车的是:机器上装的是 64 位 Office,驱动也是 64 位的,但 Visual Studio 默认生成的程序是 AnyCPU,调试时加载了 32 位进程,结果报错“未在本地计算机上注册 Microsoft.ACE.OLEDB.12.0 提供程序”。

所以第一件事,把项目的生成目标平台改好。操作如下:

  • 在解决方案资源管理器右键项目 → 属性
  • 切到“生成”选项卡
  • 把“平台目标”改成 x86 或 x64
  • 或者用“配置管理器”新建一个 x86 平台

单机小工具推荐 x86,因为很多老机器的 Office 和 Access 引擎都是 32 位的,兼容性最好。如果你的程序一定要跑 64 位,就统一装 64 位驱动,别混用。

1.2 连接字符串怎么写

连接字符串是整段代码的命门。一个标准的 Access 连接字符串长这样:

string connStr = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\Demo\student.accdb;";

各部分的含义:

  • Provider:指定 OLEDB 提供程序,ACE 12.0 是 2007 以后版本的通用驱动。
  • Data Source:Access 数据库文件的完整路径。
  • Persist Security Info:默认为 False 就可以,不需要改成 True。
  • 如果数据库设置了密码,连接字符串追加Jet OLEDB:Database Password=你的密码;

注意Data Source前面加@符号,避免路径里的反斜杠被当成转义符。如果路径里有空格,也直接写在引号里,不用额外处理。

1.3 引用哪些命名空间

在代码文件顶部加这几行:

using System.Data; using System.Data.OleDb;

所有 Access 操作都围绕OleDbConnectionOleDbCommandOleDbDataAdapterOleDbDataReader这几个类展开。它们都来自System.Data.OleDb,这是 .NET Framework 和 .NET Core/.NET 5+ 都内置支持的。

2. 数据库设计与代码结构思路

2.1 先建库建表,再写代码

很多新手一上来就写代码,数据库还没建,连接字符串里的路径随便填,然后报错“文件找不到”或者“不是有效路径”。靠谱顺序是:

  1. 用 Access 软件建好 .accdb 文件
  2. 在里面建表、建字段
  3. 填几条测试数据
  4. 再回到 Visual Studio 写连接和增删改查

如果你没有装 Access,也可以让程序在启动时自动建库建表。这样部署的时候不用额外依赖数据库文件,对用户更友好。建库的代码示例:

string dataDir = AppDomain.CurrentDomain.BaseDirectory; string dbPath = Path.Combine(dataDir, "student.accdb"); if (!File.Exists(dbPath)) { var catalog = new ADOX.Catalog(); catalog.Create("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + dbPath + ";"); MessageBox.Show("数据库已创建"); }

这段代码里用到了 ADOX,需要添加 COM 引用Microsoft ADO Ext. 6.0 for DDL and Security,或者直接优先建议用 Access 手动建,简单不出错。程序里自动建表也是可以的:

using (OleDbConnection conn = new OleDbConnection(connStr)) { conn.Open(); string sql = @"CREATE TABLE Student ( ID AUTOINCREMENT PRIMARY KEY, Name VARCHAR(50), Age INT, Score DOUBLE, Birthday DATETIME)"; OleDbCommand cmd = new OleDbCommand(sql, conn); cmd.ExecuteNonQuery(); }

这里把ID设成AUTOINCREMENT,就是 Access 里的“自动编号”,每次插入新记录会自动生成一个不重复的编号,适合做课程设计里的学号、工号场景。

2.2 字段类型映射别踩坑

Access 的数据类型和 C# 对应关系大致如下:

Access 类型存储内容C# 对应类型
文本短字符串string
备注长文本string
数字(长整型)整数int
数字(单精度/双精度)小数float/double
日期/时间日期DateTime
是/否布尔bool
OLE 对象二进制byte[]

特别注意,Access 的“数字”类型默认是长整型,取值不能超过 int 的范围。插入小数时要提前把字段类型设为“双精度”或“小数”,否则数据会被静默截断。

2.3 为什么用参数化而不是拼接字符串

增删改查接用户输入时,最忌讳直接把值拼进 SQL 字符串。比如:

string sql = "INSERT INTO Student(Name, Age) VALUES('" + nameTextBox.Text + "', " + ageTextBox.Text + ")";

这样写,用户输入一个' OR '1'='1就能把你的 SQL 搞乱,甚至删掉整张表的数据,这就是著名的 SQL 注入。Access 数据库同样有这个问题。

正确写法是用参数:

string sql = "INSERT INTO Student(Name, Age) VALUES(@Name, @Age)"; OleDbCommand cmd = new OleDbCommand(sql, conn); cmd.Parameters.AddWithValue("@Name", nameTextBox.Text); cmd.Parameters.AddWithValue("@Age", Convert.ToInt32(ageTextBox.Text));

参数名用@开头,和 SQL Server 的写法一致,但底层传递方式不同。用参数化之后,引号、中文、特殊字符都不用担心,这是必须养成的习惯。

3. 增删改查完整代码实现

3.1 封装数据库连接辅助类

写一个小工具类,把公共逻辑抽出来,后面所有窗体都能复用。这是实际项目里最实用的做法。

public class AccessHelper { private string connStr; public AccessHelper(string dbPath) { connStr = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + dbPath + ";"; } public DataTable Query(string sql, params OleDbParameter[] parameters) { using (OleDbConnection conn = new OleDbConnection(connStr)) { conn.Open(); using (OleDbCommand cmd = new OleDbCommand(sql, conn)) { if (parameters != null) cmd.Parameters.AddRange(parameters); using (OleDbDataAdapter adapter = new OleDbDataAdapter(cmd)) { DataTable dt = new DataTable(); adapter.Fill(dt); return dt; } } } } public int Execute(string sql, params OleDbParameter[] parameters) { using (OleDbConnection conn = new OleDbConnection(connStr)) { conn.Open(); using (OleDbCommand cmd = new OleDbCommand(sql, conn)) { if (parameters != null) cmd.Parameters.AddRange(parameters); return cmd.ExecuteNonQuery(); } } } }

这里用using包裹连接和命令,可以确保用完自动释放,避免连接没关导致数据库文件被锁。文件锁是 Access 开发里最常见的坑,后面会单独讲。

3.2 添加数据 INSERT

添加数据的思路:连接数据库 → 写 INSERT 语句 → 添加参数 → 执行。

string dbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "student.accdb"); AccessHelper helper = new AccessHelper(dbPath); string sql = "INSERT INTO Student(Name, Age, Score, Birthday) VALUES(@Name, @Age, @Score, @Birthday)"; OleDbParameter[] ps = { new OleDbParameter("@Name", nameTextBox.Text), new OleDbParameter("@Age", Convert.ToInt32(ageTextBox.Text)), new OleDbParameter("@Score", Convert.ToDouble(scoreTextBox.Text)), new OleDbParameter("@Birthday", Convert.ToDateTime(birthdayPicker.Value)) }; int rows = helper.Execute(sql, ps); if (rows > 0) MessageBox.Show("添加成功");

几个容易出问题的细节:

  • 文本框里如果是空字符串,直接Convert.ToInt32会抛异常,提交前先做输入校验。
  • 日期值建议用DateTimePicker,避免用户手动输入各种格式的日期,解析时容易出错。
  • 插入语句不要写ID字段,它是自动编号,交给数据库生成。

3.3 查询数据 SELECT 和显示

查询有两种常见方式。一种是只查一条记录或一个聚合结果,用ExecuteScalar;另一种是查多条记录显示在表格里,用DataTable配合DataGridView

查单条记录示例:

string sql = "SELECT COUNT(*) FROM Student WHERE Age > @Age"; using (OleDbConnection conn = new OleDbConnection(connStr)) { conn.Open(); using (OleDbCommand cmd = new OleDbCommand(sql, conn)) { cmd.Parameters.AddWithValue("@Age", 18); int count = Convert.ToInt32(cmd.ExecuteScalar()); MessageBox.Show("成年学生数量:" + count); } }

查列表并显示到DataGridView

string sql = "SELECT ID, Name, Age, Score, Birthday FROM Student"; DataTable dt = helper.Query(sql); dataGridView1.DataSource = dt;

这里有个细节:Access 支持SELECT *,但实际项目不建议写*。原因有两个,一是如果表结构变了,程序里取字段名会乱;二是字段顺序不可控,跟界面不一定对得上。尽量明确写出需要的字段。

如果要模糊查询,用LIKE

string sql = "SELECT ID, Name, Age, Score, Birthday FROM Student WHERE Name LIKE @Keyword"; OleDbParameter[] ps = { new OleDbParameter("@Keyword", "%" + keywordTextBox.Text.Trim() + "%") }; DataTable dt = helper.Query(sql, ps);

Access 的 LIKE 通配符是百分号,这点和 SQL Server 一样。

3.4 修改数据 UPDATE

修改的关键在于 WHERE 条件,必须准确锁到要更新的那条记录。一般用主键 ID 作为条件,不要用姓名。因为姓名可能重复,一改就是好几条记录。

string sql = "UPDATE Student SET Name=@Name, Age=@Age, Score=@Score, Birthday=@Birthday WHERE ID=@ID"; OleDbParameter[] ps = { new OleDbParameter("@Name", nameTextBox.Text), new OleDbParameter("@Age", Convert.ToInt32(ageTextBox.Text)), new OleDbParameter("@Score", Convert.ToDouble(scoreTextBox.Text)), new OleDbParameter("@Birthday", Convert.ToDateTime(birthdayPicker.Value)), new OleDbParameter("@ID", currentId) }; int rows = helper.Execute(sql, ps); if (rows > 0) MessageBox.Show("修改成功");

currentId通常来自用户在DataGridView里选中行的 ID 值。取选中行 ID 的写法:

if (dataGridView1.CurrentRow != null) { int currentId = Convert.ToInt32(dataGridView1.CurrentRow.Cells["ID"].Value); }

注意CurrentRow可能为 null,特别是表格刚加载完还没点击任何行的时候。取之前一定要判空。

3.5 删除数据 DELETE

删除按 ID 删,避免误删。

string sql = "DELETE FROM Student WHERE ID=@ID"; OleDbParameter[] ps = { new OleDbParameter("@ID", currentId) }; int rows = helper.Execute(sql, ps); if (rows > 0) MessageBox.Show("删除成功");

删除操作最好加确认提示,防止手滑。真删了 Access 里没有回滚的概念,也没有 SQL Server 那种事务日志可以恢复,做好确认弹窗比什么都强。

DialogResult dr = MessageBox.Show("确定删除这条记录吗?", "删除确认", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (dr == DialogResult.Yes) { // 执行删除 }

3.6 带事务的多步骤操作

有些业务要求“要么都成功,要么都失败”。比如同时删除一个学生和他的选课记录,删了学生没删选课表,数据就不完整了。这时候用事务:

using (OleDbConnection conn = new OleDbConnection(connStr)) { conn.Open(); using (OleDbTransaction tx = conn.BeginTransaction()) { try { string sql1 = "DELETE FROM Score WHERE StudentID=@StudentID"; OleDbCommand cmd1 = new OleDbCommand(sql1, conn, tx); cmd1.Parameters.AddWithValue("@StudentID", studentId); cmd1.ExecuteNonQuery(); string sql2 = "DELETE FROM Student WHERE ID=@ID"; OleDbCommand cmd2 = new OleDbCommand(sql2, conn, tx); cmd2.Parameters.AddWithValue("@ID", studentId); cmd2.ExecuteNonQuery(); tx.Commit(); } catch { tx.Rollback(); throw; } } }

OleDbCommand构造函数里可以传事务对象,代表这条命令在事务中执行。中途任何一个步骤异常,回滚到事务开始前。

4. 学生信息管理小工具:完整流程跑一遍

4.1 界面设计

为了让上面的代码串起来,我做一个最简单的学生信息管理窗体。界面放这些控件:

  • 三个文本框:姓名、年龄、分数
  • 一个日期选择器:生日
  • 四个按钮:添加、修改、删除、查询
  • 一个关键字文本框 + 一个查找按钮
  • 一个 DataGridView 用于显示数据

这个结构是入门增删改查最典型的排布。不用做多复杂,逻辑走通以后再往后加功能。

4.2 窗体加载时刷新表格

private void Form1_Load(object sender, EventArgs e) { LoadData(); } private void LoadData() { string dbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "student.accdb"); AccessHelper helper = new AccessHelper(dbPath); string sql = "SELECT ID, Name, Age, Score, Birthday FROM Student"; DataTable dt = helper.Query(sql); dataGridView1.DataSource = dt; // 隐藏自动编号列,避免用户修改主键 if (dataGridView1.Columns.Contains("ID")) dataGridView1.Columns["ID"].Visible = false; }

这里把 ID 列隐藏了,因为它是自动编号,不应该让用户看到或修改。但代码里取值仍然需要它。

4.3 新增记录

private void btnAdd_Click(object sender, EventArgs e) { if (!ValidateInput()) return; string dbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "student.accdb"); AccessHelper helper = new AccessHelper(dbPath); string sql = "INSERT INTO Student(Name, Age, Score, Birthday) VALUES(@Name, @Age, @Score, @Birthday)"; OleDbParameter[] ps = { new OleDbParameter("@Name", nameTextBox.Text.Trim()), new OleDbParameter("@Age", Convert.ToInt32(ageTextBox.Text.Trim())), new OleDbParameter("@Score", Convert.ToDouble(scoreTextBox.Text.Trim())), new OleDbParameter("@Birthday", birthdayPicker.Value) }; int rows = helper.Execute(sql, ps); if (rows > 0) { MessageBox.Show("添加成功"); LoadData(); ClearInput(); } }

ValidateInput可以这样写:

private bool ValidateInput() { if (string.IsNullOrWhiteSpace(nameTextBox.Text)) { MessageBox.Show("姓名不能为空"); return false; } if (!int.TryParse(ageTextBox.Text, out _)) { MessageBox.Show("年龄必须是整数"); return false; } if (!double.TryParse(scoreTextBox.Text, out _)) { MessageBox.Show("分数必须是数字"); return false; } return true; }

先校验再入库,比提交数据库抛异常再提示要友好得多。

4.4 双击表格填充修改框

用户点击表格某一行,把该行的数据显示到上方的文本框里,这样修改时可以直接看到当前值。

private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) { if (dataGridView1.CurrentRow == null) return; nameTextBox.Text = dataGridView1.CurrentRow.Cells["Name"].Value.ToString(); ageTextBox.Text = dataGridView1.CurrentRow.Cells["Age"].Value.ToString(); scoreTextBox.Text = dataGridView1.CurrentRow.Cells["Score"].Value.ToString(); birthdayPicker.Value = Convert.ToDateTime(dataGridView1.CurrentRow.Cells["Birthday"].Value); }

这里有个坑:如果数据库里的生日字段是空的,Convert.ToDateTime就会炸。所以建表时给生日字段设置默认值,或者读取时判空:

if (dataGridView1.CurrentRow.Cells["Birthday"].Value != DBNull.Value) birthdayPicker.Value = Convert.ToDateTime(dataGridView1.CurrentRow.Cells["Birthday"].Value);

4.5 按条件模糊查询

private void btnSearch_Click(object sender, EventArgs e) { string keyword = keywordTextBox.Text.Trim(); string dbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "student.accdb"); AccessHelper helper = new AccessHelper(dbPath); string sql = "SELECT ID, Name, Age, Score, Birthday FROM Student WHERE Name LIKE @Keyword"; OleDbParameter[] ps = { new OleDbParameter("@Keyword", "%" + keyword + "%") }; DataTable dt = helper.Query(sql, ps); dataGridView1.DataSource = dt; }

如果关键字为空,就当作查全部。这个逻辑可以优化一下:

string sql = "SELECT ID, Name, Age, Score, Birthday FROM Student"; if (!string.IsNullOrWhiteSpace(keyword)) { sql += " WHERE Name LIKE @Keyword"; // 添加参数并执行 }

4.6 修改与删除联动

修改、删除按钮的代码前面已经给了,核心点是记住当前选中的行 ID。可以定义一个窗体级变量:

private int currentId = -1;

CellClick事件里赋值:

currentId = Convert.ToInt32(dataGridView1.CurrentRow.Cells["ID"].Value);

修改按钮里直接使用这个变量。如果currentId == -1,说明用户没有选中任何记录,直接提示。

这个完整流程跑通之后,增删改查就不只是会写四个方法,而是一个连得起来的小系统。

5. 高频报错与排查方案

5.1 未在本地计算机上注册 ACE.OLEDB.12.0 提供程序

这个是最常见的。原因只有一个:程序和驱动位数对不上。

排查步骤:

  1. 在代码里第一行加Environment.Is64BitProcess,看程序跑的是 32 位还是 64 位
  2. 检查安装的 ACE 驱动位数
  3. 项目平台目标改成一致

如果项目是 AnyCPU,在 x64 系统上默认以 64 位进程加载,如果是 32 位驱动那就报错。最省事的办法是固定 x86。

5.2 操作必须使用可更新查询

这个报错通常发生在执行UPDATEDELETE时。常见原因有两个:

  • 查询语句里做了 JOIN,连接了多张表,Access 无法确定要更新哪张表
  • 数据库文件被设置为只读

排查方式:

  • 更新语句只操作一张表,JOIN 的需求改成先查一张表,再用结果更新另一张
  • 右键 .accdb 文件,检查“只读”属性是否被勾选
  • 把文件复制到本地再试,有些权限策略会导致 Access 无法写文件

5.3 数据库文件被锁,出现“文件正由另一进程使用”

Access 是基于文件的数据库,没有独立的数据库服务。程序打开连接后,如果连接没有关闭,文件会一直处于锁定状态,其他程序或本程序第二次打开就会冲突。

排查方式:

  • 检查是否正确释放连接,统一用using
  • 检查窗体是否留下未结束的OleDbConnection对象。
  • 如果程序调试时崩溃,旧进程没退,打开任务管理器结束残留进程。
  • 不要把 Access 数据库文件放在某些云同步目录里,同步工具会频繁访问文件,容易造成无法写入。

这类文件锁问题在 SQL Server 里几乎不存在,这是 Access 自带的天生限制。如果你发现你的工具经常出现文件锁,说明数据量或并发量已经超出 Access 合适范围,需要考虑换数据库了。

5.4 找不到可安装的 ISAM

这个报错一般是连接字符串里的Provider写错,或者数据库扩展名和内容不匹配。比如 .mdb 文件用了 ACE 驱动,老版本 .mdb 应该用Microsoft.Jet.OLEDB.4.0;如果用了 ACE 驱动访问,记得检查扩展名。

如果是 .mdb 文件,连接字符串写成:

string connStr = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\Demo\student.mdb;";

Jet 驱动在 64 位系统上支持比较差,老项目尽量迁移到 .accdb 格式。

5.5 错误 1045 Access denied

注意这个错误码是 MySQL 的,不是 Access 的。如果你在跑连接字符串时看到它,说明连接字符串或者代码里实际连的是 MySQL,而不是 Access 文件。把ProviderData Source再检查一遍。

5.6 添加数据后 ID 不连续

Access 自动编号默认是按顺序递增的,但你删除记录再插入,它不会回填之前删掉的编号。比如删掉 ID=3 的记录,下次插入可能变成 ID=4。

这不是 bug,是设计如此。课程设计里如果要求学号连续,就不要用自动编号,改为手动生成编号,程序里查询最大值再加一:

string sql = "SELECT MAX(ID) FROM Student"; object result = cmd.ExecuteScalar(); int newId = 1; if (result != DBNull.Value && result != null) newId = Convert.ToInt32(result) + 1;

这个方案在单机使用没问题,并发插入时会有问题,但 Access 本身就不适合高并发,个人工具和课程设计够用了。

5.7 调试时进程退出异常 0xc0000005

这个不是 Access 特有问题,而是内存访问冲突。C# 程序出现这类异常,通常和调用本机 DLL 有关。OLEDB 就是 COM 组件,接近本机层面,最容易踩。

常见的触发场景是:连接没释放、驱动版本混乱、同一进程里多次创建和销毁连接。建议先统一驱动位数,再整理代码里所有连接的使用,确保没有跨线程共用一个连接对象。

5.8 高频问题速查表

报错信息可能原因解决方案
未在本地计算机上注册提供程序驱动位数不一致先确认 x86/x64,装对应驱动
操作必须使用可更新查询多表 JOIN 或文件只读改为单表更新,检查文件属性
找不到可安装的 ISAMProvider 错误或文件格式不对检查连接字符串和数据库扩展名
文件正由另一进程使用连接未释放用 using,清理残留进程
无法更新数据库,数据库或对象为只读权限不足把文件复制到本地,关闭只读
不能锁定文件网络路径或同步目录本地磁盘目录,排除云同步目录

6. 读取和显示单条记录的字段数据

有时候不想用 DataGridView,只查一条记录并填入文本框,这也是上位机和工具软件里常见的需求。比如用扫码枪扫一下学号,自动带出学生信息。

string sql = "SELECT Name, Age, Score, Birthday FROM Student WHERE ID=@ID"; using (OleDbConnection conn = new OleDbConnection(connStr)) { conn.Open(); using (OleDbCommand cmd = new OleDbCommand(sql, conn)) { cmd.Parameters.AddWithValue("@ID", idTextBox.Text.Trim()); using (OleDbDataReader reader = cmd.ExecuteReader()) { if (reader.Read()) { nameTextBox.Text = reader["Name"].ToString(); ageTextBox.Text = reader["Age"].ToString(); scoreTextBox.Text = reader["Score"].ToString(); birthdayPicker.Value = Convert.ToDateTime(reader["Birthday"]); } else { MessageBox.Show("没有找到这条记录"); } } } }

ExecuteReader返回一个只读的数据流,适合读少量记录或者单条记录。和DataTable相比,DataReader更轻量,但只能向前读,不能用下标随机访问多行。两者使用场景别搞混。

7. 扩展:用 Dapper 封装 Access 操作

如果项目稍微大一点,手写OleDbCommand太啰嗦,可以考虑 Dapper。Dapper 是轻量级 ORM,支持 MySQL、SQL Server、SQLite,也支持 Access。

需要先通过 NuGet 安装 Dapper。连接对象可以保持原来的OleDbConnection,Dapper 的扩展方法会自动处理命令和参数:

using Dapper; public class Student { public int ID { get; set; } public string Name { get; set; } public int Age { get; set; } public double Score { get; set; } public DateTime Birthday { get; set; } } public List<Student> GetStudents(string keyword) { using (OleDbConnection conn = new OleDbConnection(connStr)) { string sql = "SELECT ID, Name, Age, Score, Birthday FROM Student WHERE Name LIKE @Keyword"; return conn.Query<Student>(sql, new { Keyword = "%" + keyword + "%" }).ToList(); } }

Dapper 会根据查询结果的字段名自动映射到Student类的属性。增删改查用Execute方法:

string sql = "INSERT INTO Student(Name, Age, Score, Birthday) VALUES(@Name, @Age, @Score, @Birthday)"; conn.Execute(sql, new { student.Name, student.Age, student.Score, student.Birthday });

注意 Dapper 在 Access 下有兼容性问题,比如 Access 的AUTOINCREMENT类型插入时不能包含 ID 字段,Dapper 的参数化写法在 Name 边上带@是可以的,但个别 Access 驱动版本对命名参数支持不好。建议先在测试机上跑通,再进入开发。

说到底,Dapper 是减少样板代码,连接和驱动底层还是 OLEDB,之前的坑一样会踩。

8. 一些经验之谈

单机版的 Access 增删改查,代码量不大,但环境坑特别多。驱动程序位数、数据库文件存放路径、连接释放、参数化查询,这四个地方做好了,整个项目就稳定了。

我自己调试这类程序时有个习惯:把connStr放到配置文件里,而不是硬编码在代码里。这样换一台电脑,数据库路径变了,只需要改一下App.config里的连接字符串,不用重新编译程序。

<connectionStrings> <add name="AccessDb" connectionString="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|student.accdb;"/> </connectionStrings>

代码里读取:

string connStr = ConfigurationManager.ConnectionStrings["AccessDb"].ConnectionString;

|DataDirectory|是一个占位符,默认指向程序运行目录,部署时把数据库文件放到 exe 同目录就能跑。

还有一个细节:发布程序时记得把 Access 数据库文件包含进去。项目里添加数据库文件后,右键属性,把“复制到输出目录”改成“如果较新则复制”。这样每次编译,新的数据库文件会自动复制到bin\Debugbin\Release目录,程序就能直接用相对路径找到文件。

最后说一句,Access 适合数据量不大、并发不高的桌面工具,一旦遇到多用户同时写文件,它的文件锁机制会很痛苦。如果你预感到项目以后会往服务器方向走,一开始就换 SQLite 或 SQL Server Express 可能更省事。但作为学习数据库开发和增删改查底层逻辑的入门项目,C# 连接 Access 这套流程仍然是一个绕不过去的经典组合。把这里面的连接管理、参数化、事务处理搞明白,以后换任何数据库都一通百通。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 10:29:03

SQLite到PostgreSQL数据迁移实战:类型映射、增量同步与踩坑记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 10:27:38

ANCF梁单元在大变形仿真中的MATLAB实现与优化

1. 项目概述这个项目研究的是单悬臂梁在重力作用下的弯曲行为仿真。作为一名长期从事结构力学仿真的工程师&#xff0c;我发现传统有限元方法在处理大变形问题时存在明显局限。而绝对节点坐标法&#xff08;ANCF&#xff09;通过引入全局坐标系下的节点位移参数&#xff0c;能够…

作者头像 李华
网站建设 2026/9/18 10:26:41

ESP32-S3+MCP协议实现AI物理交互系统

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 10:25:30

DeepSeek表格语义解析:让CSV/Excel从格式依赖走向语义理解

简介&#xff1a;本资源是一份面向Python开发者与数据分析师的实战型技术文档&#xff0c;聚焦DeepSeek大模型在结构化数据解析场景中的创新应用&#xff0c;解决CSV与Excel自动化报告生成中的格式适配、内容提取与智能填充难题。文档共26页PDF&#xff0c;完整覆盖从基础读写&…

作者头像 李华
网站建设 2026/9/18 10:24:33

论文查重技术解析:免费高效与安全并重

1. 论文查重行业的痛点与用户焦虑作为一名经历过本科、硕士到博士论文洗礼的"学术老兵"&#xff0c;我深知论文查重环节带来的心理压力。记得硕士论文预答辩前一周&#xff0c;我连续三天熬夜修改论文&#xff0c;每次查重都要精打细算地选择最"划算"的检测…

作者头像 李华