简介:这份资源是Visual Studio 2005环境下Access数据库开发的经典案例配套光盘,面向正在学习C#与Access结合开发的中初级程序员、计算机专业学生及需要课程设计或毕业设计参考的开发者。光盘内容围绕实际项目场景展开,帮助读者理解数据库表结构设计、数据访问层封装、窗体交互与报表输出等核心环节,解决从理论到动手实践之间的衔接问题。压缩包为zip格式,整体约11.51MB,文件总数与具体类型明细上游暂未提供,但结合案例光盘的常见组织方式,通常包含可运行的示例工程、数据库文件及必要的说明文档,便于直接打开对照学习。目前已有255人学习下载,说明该案例在同类教学资源中具有一定参考价值。读者可从中获取完整的项目实现思路、关键代码组织方式以及Access在WinForm应用中的典型用法,适合作为动手练手与查漏补缺的辅助材料。
1. 从一张老光盘说起:Access 2005 经典案例到底能跑出什么
接手一套遗留的进销存系统时,数据库文件是.mdb格式,运行环境是 Windows XP 加 Office 2003。客户要求把数据迁移到新机器上,但原开发人员早已失联,只留下一张标注着“vs#2005Access数据库开发经典案例”的光盘。这张光盘里装的不是源码包,而是一整套用 Visual Studio 2005 配合 Access 做的桌面管理程序案例集,涵盖库存、工资、图书借阅等场景。很多人以为 Access 只是单机玩具,但那个年代的经典案例恰恰展示了它在中小型 MIS 里的完整落地路径:窗体绑定、报表打印、VBA 事务、ADO 连接池。如果你手头有类似的老系统要维护、要复现、要迁移,或者想理解早期快速应用开发(RAD)的工程组织方式,这些案例就是活化石。它们能解决的不是高并发问题,而是“如何用最少依赖把业务跑起来”的问题,适合需要接手遗留系统、做数据接口对接、或者研究桌面数据库架构的从业者。
2. 拆开光盘目录:Access 2005 案例的工程结构与运行依赖
2.1 典型目录布局与文件类型识别
一张标准的 VS2005 + Access 案例光盘,根目录通常包含Setup、Source、Database、Docs四个文件夹。Source下按案例名分子目录,每个子目录里是.sln和.vbproj或.csproj,以及bin、obj、My Project等编译产物。Database里放.mdb或.adp文件,有时还有.ldb锁文件残留。Docs里是.chm帮助文档或.doc设计说明。识别版本的关键是看.sln文件头部的Format Version,VS2005 对应的是9.00,而.vbproj里的TargetFrameworkVersion通常是v2.0。如果光盘里出现.mdf或.ldf,那说明案例用的是 SQL Server Express 而不是纯 Access,需要区分对待。
2.2 运行环境搭建:从零把案例跑起来
先装 Visual Studio 2005 或更高版本(VS2019 也能打开,但需要升级向导),再装 Access 2003 或 Access 2007 运行时。如果只想跑程序不装 Office,可以单独安装AccessDatabaseEngine.exe,注意选 32 位版本,因为 VS2005 默认编译目标平台是 x86。连接字符串一般写在App.config或模块常量里,形如:
<!-- App.config 中的连接字符串配置 --> <configuration> <appSettings> <!-- 使用相对路径指向 Database 目录下的 mdb 文件 --> <add key="ConnStr" value="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\Database\Inventory.mdb;Jet OLEDB:Database Password=;"/> </appSettings> </configuration>逻辑说明:Provider用Microsoft.Jet.OLEDB.4.0对应 Access 2003 及更早格式,如果文件是.accdb则要换成Microsoft.ACE.OLEDB.12.0。Data Source里的|DataDirectory|是 .NET 的数据目录宏,编译后指向bin\Debug,所以要把Database文件夹复制到输出目录,或者在项目属性里设置“复制到输出目录”。参数Jet OLEDB:Database Password如果原库有密码就填上,没有就留空。常见翻车点是 64 位系统上默认以 64 位运行程序,而 Jet 引擎只有 32 位,会报“未在本地计算机上注册 Microsoft.Jet.OLEDB.4.0”,解决办法是在项目属性里把目标平台改成x86。
2.3 数据访问层:ADO 与数据绑定控件的配合
案例里最常出现的模式是DataSet+DataAdapter+BindingSource。在窗体设计器里拖一个DataGridView,绑定到BindingSource,再在代码里填充DataSet。下面是一个典型的加载逻辑:
' 从 Access 读取库存表并绑定到界面 Dim conn As New OleDbConnection(My.Settings.ConnStr) Dim adapter As New OleDbDataAdapter("SELECT * FROM 库存表", conn) Dim ds As New DataSet() Try conn.Open() adapter.Fill(ds, "库存表") ' 填充数据集,表名与 SQL 中的表名对应 BindingSource1.DataSource = ds.Tables("库存表") DataGridView1.DataSource = BindingSource1 Catch ex As OleDbException MessageBox.Show("读取失败:" & ex.Message) Finally conn.Close() End Try逻辑说明:OleDbDataAdapter的Fill方法会自动打开和关闭连接,但显式Open可以更早暴露连接问题。BindingSource作为中间层,方便后续做筛选和排序。参数方面,SELECT *在案例里很常见,但生产环境建议只取需要的列,减少内存占用。如果表名包含空格或中文,SQL 里要用方括号括起来,比如[库存 表]。另一个坑是 Access 的OleDb不支持命名参数,只能用?占位符,且参数顺序必须与 SQL 中出现的顺序一致,否则会报“参数未指定”。
3. 把案例改造成可维护系统:从单机 mdb 到分层架构
3.1 拆分数据层:把 SQL 从窗体代码里抽出来
原案例为了演示方便,经常把 SQL 字符串直接写在按钮事件里。维护时第一步就是抽到独立的DataAccess类。下面是一个 C# 版本的示例:
// DataAccess.cs 统一处理数据库连接和命令 public class DataAccess { private static readonly string ConnStr = ConfigurationManager.AppSettings["ConnStr"]; // 执行查询并返回 DataTable public static DataTable ExecuteQuery(string sql, params OleDbParameter[] parameters) { using (var conn = new OleDbConnection(ConnStr)) using (var cmd = new OleDbCommand(sql, conn)) { if (parameters != null) cmd.Parameters.AddRange(parameters); var dt = new DataTable(); conn.Open(); using (var reader = cmd.ExecuteReader()) { dt.Load(reader); // 一次性加载,适合小数据量 } return dt; } } // 执行增删改,返回受影响行数 public static int ExecuteNonQuery(string sql, params OleDbParameter[] parameters) { using (var conn = new OleDbConnection(ConnStr)) using (var cmd = new OleDbCommand(sql, conn)) { if (parameters != null) cmd.Parameters.AddRange(parameters); conn.Open(); return cmd.ExecuteNonQuery(); } } }逻辑说明:using确保连接和命令对象及时释放,避免 Access 文件被锁定。OleDbParameter的顺序必须与 SQL 中的?一一对应,所以调用时要注意传参顺序。DataTable.Load适合数据量小的场景,如果表很大,应该改用DataReader逐行处理。参数化查询能防止 SQL 注入,也能避免日期和文本格式的解析错误。注意 Access 的OleDb不支持存储过程,所有逻辑都得在代码里拼 SQL 或调用 VBA 模块。
3.2 事务处理:Access 里的原子操作怎么写
Access 支持事务,但语法和 SQL Server 不同。在 ADO.NET 里用OleDbTransaction显式控制:
// 批量更新库存,保证要么全成功要么全回滚 public static void UpdateStock(List<StockItem> items) { using (var conn = new OleDbConnection(ConnStr)) { conn.Open(); using (var trans = conn.BeginTransaction()) using (var cmd = conn.CreateCommand()) { cmd.Transaction = trans; try { foreach (var item in items) { cmd.CommandText = "UPDATE 库存表 SET 数量 = ? WHERE 编号 = ?"; cmd.Parameters.Clear(); cmd.Parameters.AddWithValue("?", item.Quantity); cmd.Parameters.AddWithValue("?", item.Code); cmd.ExecuteNonQuery(); } trans.Commit(); // 全部成功才提交 } catch (Exception) { trans.Rollback(); // 任何一步失败就回滚 throw; } } } }逻辑说明:BeginTransaction后,所有命令必须设置Transaction属性,否则会报“事务上下文已禁用”。Parameters.Clear()在循环里必须调用,否则参数会不断累加。Access 的事务隔离级别默认是读已提交,但并发写入时容易触发表锁,所以批量操作尽量放在非高峰时段。如果 mdb 文件放在网络共享上,事务性能会急剧下降,建议先复制到本地再处理。
3.3 报表与打印:把 DataSet 喂给 ReportViewer
VS2005 自带的Microsoft Reporting控件可以绑定DataSet生成报表。步骤是:新建.rdlc文件,在报表设计器里添加数据集,字段从DataSet架构拖拽。运行时用ReportViewer.LocalReport.DataSources.Add绑定:
// 加载报表并绑定数据 var reportViewer1 = new ReportViewer(); reportViewer1.LocalReport.ReportPath = "Reports/InventoryReport.rdlc"; var dt = DataAccess.ExecuteQuery("SELECT * FROM 库存表"); var rds = new ReportDataSource("InventoryDataSet", dt); reportViewer1.LocalReport.DataSources.Clear(); reportViewer1.LocalReport.DataSources.Add(rds); reportViewer1.RefreshReport();逻辑说明:ReportDataSource的名称必须与.rdlc里定义的数据集名称完全一致,区分大小写。RefreshReport会触发渲染,如果数据量为零会显示空白页。打印时注意设置PageSettings的纸张大小和边距,Access 案例里经常忽略这个,导致打印出来缺列。如果报表要导出 PDF,需要额外安装Microsoft.ReportViewer的对应版本,且注意 32 位和 64 位的兼容性。
4. 避坑与排查:Access 2005 案例维护中的五个血泪教训
4.1 现象:程序启动报“未在本地计算机上注册 Microsoft.Jet.OLEDB.4.0”
原因:项目目标平台是 Any CPU,在 64 位系统上以 64 位进程运行,而 Jet 引擎只有 32 位版本。解决:在项目属性 → 生成 → 目标平台改为x86,或者改用Microsoft.ACE.OLEDB.12.0并安装 64 位 Access 数据库引擎。注意 ACE 引擎对.mdb的兼容性不如 Jet,某些旧函数会报错。
4.2 现象:多用户同时打开 mdb 文件时,写入操作随机失败
原因:Access 的文件锁机制基于.ldb文件,网络共享环境下锁信息同步延迟,导致并发冲突。解决:把 mdb 拆成前端(窗体、查询)和后端(仅表),前端分发给每个用户,后端放在共享目录,通过链接表访问。或者改用 SQL Server Express 作为后端,Access 只做前端。如果必须用 mdb,设置Jet OLEDB:Database Locking Mode为1(乐观锁),并缩短事务时间。
4.3 现象:日期字段查询结果为空或格式错乱
原因:Access 的日期字面量必须用#包裹,如#2024-01-01#,而参数化查询时OleDbParameter的OleDbType要设为DBDate。如果直接拼字符串,系统区域设置不同会导致解析失败。解决:统一用参数化查询,并显式指定类型:
cmd.Parameters.Add(new OleDbParameter("?", OleDbType.DBDate) { Value = dateValue });另外,Access 的Now()返回的是本地时间,如果跨时区使用要注意转换。
4.4 现象:窗体设计器打开时报“未找到类型”或“无法加载组件”
原因:案例光盘里的项目引用了绝对路径的程序集,或者某些 COM 组件(如mscomctl.ocx)未注册。解决:在解决方案资源管理器里检查引用,删除失效引用后重新添加。对于 COM 组件,用管理员权限运行regsvr32 mscomctl.ocx。如果还是不行,把项目文件里的<Reference>节点手动改成相对路径或 NuGet 包。
4.5 现象:迁移到新机器后,报表打印乱码或字体丢失
原因:.rdlc里指定的字体在新机器上不存在,或者系统区域设置不是中文。解决:在报表设计器里把字体改成通用字体(如宋体、微软雅黑),并在代码里设置LocalReport.ExecuteReportInCurrentAppDomain为true。如果打印出来是方框,检查打印机驱动是否支持中文,必要时先导出为 PDF 再打印。
5. 进阶技巧:用现代工具链给 Access 案例做数据迁移与接口封装
接手老案例的最终目标往往不是继续维护它,而是把数据迁到新系统。我一般会先用OleDb把表结构和数据导出为 SQLite 或 SQL Server,再用 Entity Framework 或 Dapper 重写数据访问层。下面是一个把 Access 表批量导出为 CSV 的脚本,方便后续导入:
// 导出 Access 所有表到 CSV,每表一个文件 public static void ExportAllTables(string mdbPath, string outputDir) { var connStr = $"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={mdbPath};"; using (var conn = new OleDbConnection(connStr)) { conn.Open(); var schema = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null); foreach (DataRow row in schema.Rows) { var tableName = row["TABLE_NAME"].ToString(); if (tableName.StartsWith("MSys")) continue; // 跳过系统表 using (var cmd = new OleDbCommand($"SELECT * FROM [{tableName}]", conn)) using (var reader = cmd.ExecuteReader()) using (var writer = new StreamWriter(Path.Combine(outputDir, $"{tableName}.csv"), false, Encoding.UTF8)) { // 写表头 for (int i = 0; i < reader.FieldCount; i++) writer.Write((i == 0 ? "" : ",") + reader.GetName(i)); writer.WriteLine(); // 写数据行 while (reader.Read()) { for (int i = 0; i < reader.FieldCount; i++) { var val = reader.IsDBNull(i) ? "" : reader.GetValue(i).ToString(); writer.Write((i == 0 ? "" : ",") + "\"" + val.Replace("\"", "\"\"") + "\""); } writer.WriteLine(); } } } } }逻辑说明:GetOleDbSchemaTable返回所有表名,过滤掉MSys开头的系统表。CSV 字段用双引号包裹,内部双引号转义为两个双引号,避免逗号冲突。编码用 UTF-8 带 BOM,方便 Excel 直接打开。如果表里有备注字段(Memo),GetValue可能返回大文本,注意内存占用。导出后可以用SqlBulkCopy导入 SQL Server,或者用sqlite3命令行导入 SQLite。
另一个技巧是把 Access 的查询对象(QueryDef)也导出来,因为很多业务逻辑藏在查询里。用OleDbSchemaGuid.Procedures可以拿到查询定义,但 Access 的查询分SELECT、ACTION、CROSSTAB等类型,需要逐个解析。我一般会先导出所有查询的 SQL 文本,人工审查后再决定哪些用视图替代、哪些用存储过程重写。
迁移过程中最容易被忽略的是 Access 的“查阅向导”字段,它在表设计里存的是数字,但显示的是文本。导出 CSV 时拿到的是数字,需要额外查对照表。解决办法是先用OleDbDataAdapter填充DataSet,然后遍历DataColumn的ExtendedProperties,但 Access 不提供这个元数据,只能靠人工记录。所以我的习惯是:拿到 mdb 后先打开 Access 界面,把每个表的字段类型和查阅属性截图存档,再写迁移脚本。这个笨办法救过我很多次,希望帮到你。
本文还有配套的精品资源,点击获取