ADO2.20 Class:经典ASP遗留系统数据库访问封装与重试实践
简介ADO2.20 Class 是一套面向 Visual C 开发者的 ActiveX Data Objects 类库最初由非中文开发者在 codeproject.com 发布旨在简化 VC 程序对 SQL Server、Oracle、Access 等数据源的访问让开发者无需深入底层 ADO 接口即可快速集成数据库操作。资源包共 3 个文件包含 1 个 cpp 实现文件、1 个 h 头文件以及 1 个 mht 说明文档压缩包约 105KB体积轻巧便于直接嵌入现有工程。其中头文件提供类定义与接口声明cpp 文件封装了 Connection、Command、Recordset 等核心对象的常用操作mht 文档则保留了原版使用说明与示例参考。目前已有 126 人学习下载适合不熟悉 ADO 或需要快速开发数据库应用的 VC 开发者通过实例化封装类即可完成连接、查询与结果集遍历将精力集中在业务逻辑上。1. ADO2.20 Class一份被低估的数据库访问封装资源如果你维护过五年前甚至更早的遗留系统大概率见过这样的代码连接字符串硬编码在业务逻辑里Recordset对象满天飞异常处理全靠On Error Resume Next兜底。ADO2.20 Class 就是针对这种场景的一份封装资源它把 ADO 2.20 时代的连接管理、命令执行、记录集遍历和错误捕获收拢成一套可复用的类模块。这不是什么新潮的 ORM 框架而是一份能让老系统少出玄学问题的工程化封装。适合谁正在维护经典 ASP 或早期 VB 项目、需要在不引入外部依赖的前提下把数据库访问层理清楚的从业者。它解决的核心问题只有一个让散落各处的 ADO 调用变成有边界、有日志、有统一出口的模块。2. 拆开这份资源类模块结构与连接管理逻辑2.1 资源里到底有什么这份资源的主体是一组类模块文件通常包含连接管理类、命令执行类和记录集处理类三个核心部分。连接管理类负责持有ADODB.Connection对象对外暴露打开、关闭和重连的方法命令执行类封装ADODB.Command处理参数化查询和存储过程调用记录集处理类则把ADODB.Recordset的遍历、字段读取和转数组操作收拢起来。常见做法是每个类对应一个.cls文件配套一个标准模块存放全局配置和工具函数。资源里一般还会带一个示例页面或测试入口用来验证连接是否正常、查询是否返回预期结果。文件格式以文本类源码为主直接导入对应的开发环境即可不需要编译或安装。2.2 连接管理为什么值得单独抽一层很多人觉得 ADO 连接就是一行Server.CreateObject(ADODB.Connection)的事没必要封装。但实际维护中连接泄漏、超时设置不一致、连接字符串散落各处才是真正的血泪经验来源。把连接管理抽成类之后至少有三个好处第一连接字符串集中在一处换数据库或改认证方式时不用全局搜索替换第二连接超时和命令超时可以在类初始化时统一设定避免某个页面卡死拖垮整个站点第三连接对象的生命周期有了明确边界配合Class_Terminate可以做到异常时自动释放。下面是一个连接管理类的典型骨架我一般会这样写 clsDbConnection.cls Private m_Conn As ADODB.Connection Private m_ConnString As String Private Sub Class_Initialize() Set m_Conn New ADODB.Connection m_Conn.ConnectionTimeout 15 m_Conn.CommandTimeout 30 End Sub Public Property Let ConnectionString(ByVal value As String) m_ConnString value End Property Public Function Open() As Boolean On Error GoTo ErrHandler If m_Conn.State adStateOpen Then Open True Exit Function End If m_Conn.Open m_ConnString Open True Exit Function ErrHandler: 记录错误日志不向上抛出原始错误 LogError Open, Err.Number, Err.Description Open False End Function Public Property Get RawConnection() As ADODB.Connection Set RawConnection m_Conn End Property Private Sub Class_Terminate() If Not m_Conn Is Nothing Then If m_Conn.State adStateOpen Then m_Conn.Close Set m_Conn Nothing End If End Sub这段代码的关键点在于Class_Terminate里的清理逻辑。VB 类的析构在对象引用计数归零时触发只要调用方没有长期持有这个连接对象连接就能在页面结束时自动关闭。ConnectionTimeout和CommandTimeout分开设置也很重要前者管建立连接后者管查询执行很多超时问题其实是命令超时而不是连接超时。2.3 命令执行与参数化查询的落地方式命令执行类要解决的核心问题是参数化。拼接 SQL 字符串在遗留系统里太常见了但它是注入风险的根源也是类型转换错误的温床。ADO 的Command对象支持Parameters.Append配合CreateParameter可以做到类型明确的参数传递。 clsDbCommand.cls Public Function ExecuteNonQuery(ByVal sql As String, _ ByVal params As Variant) As Long Dim cmd As ADODB.Command Set cmd New ADODB.Command Set cmd.ActiveConnection m_Conn cmd.CommandType adCmdText cmd.CommandText sql Dim i As Long For i LBound(params) To UBound(params) cmd.Parameters.Append cmd.CreateParameter( _ p i, adVarWChar, adParamInput, 255, params(i)) Next i cmd.Execute , , adExecuteNoRecords ExecuteNonQuery cmd.Parameters(p0).Value Set cmd Nothing End Function参数说明adVarWChar对应 Unicode 字符串adParamInput表示输入参数长度 255 是常见默认值实际使用时按字段长度调整。adExecuteNoRecords告诉 ADO 这次执行不返回记录集能省掉不必要的开销。注意ExecuteNonQuery的返回值这里取的是第一个参数的值适合存储过程带输出参数的场景如果是普通 INSERT应该改用RecordsAffected属性。2.4 记录集转数组的实用技巧记录集处理类里最实用的功能是GetRows。它一次性把结果集拉成二维数组断开记录集后仍然可以遍历对分页和缓存场景很友好。Public Function QueryToArray(ByVal sql As String) As Variant Dim rs As ADODB.Recordset Set rs m_Conn.Execute(sql) If rs.EOF And rs.BOF Then QueryToArray Array() Else QueryToArray rs.GetRows() End If rs.Close Set rs Nothing End FunctionGetRows返回的数组第一维是字段第二维是行。遍历时用arr(fieldIndex, rowIndex)访问。空结果集要单独判断否则GetRows返回空数组调用方直接取下标会翻车。这个细节在资源自带的示例里通常有体现但很多人导入后不看示例直接套自己的旧代码结果就在空数据上栽跟头。3. 从导入到跑通一份可复现的接入流程3.1 环境准备与文件放置接入之前先确认运行环境。ADO 2.20 对应的组件版本在多数 Windows 服务器上默认可用不需要额外安装。开发环境里如果用的是经典 ASP把.cls文件放在站点目录下通过!--#include fileclsDbConnection.cls--引入如果是 VB6 或 VBA 项目直接在工程里添加类模块。常见做法是建一个lib目录专门放这些类文件再建一个config目录放连接字符串配置。连接字符串不要写死在类里用标准模块的全局变量或外部配置文件读取。 modConfig.bas Public Const DB_CONN_STRING ProviderSQLOLEDB;Data Source127.0.0.1; _ Initial CatalogTestDb;User Idapp_user;Passwordapp_pass;提示连接字符串里的密码如果明文存放至少要把配置文件放在 Web 根目录之外避免被直接下载。3.2 最小可运行示例导入类文件后写一个最简单的测试页面验证链路是否通。!-- test_connection.asp -- % Dim db, result Set db New clsDbConnection db.ConnectionString DB_CONN_STRING If db.Open() Then result db.QueryToArray(SELECT TOP 5 id, name FROM users) If IsArray(result) Then Dim i For i 0 To UBound(result, 2) Response.Write result(0, i) - result(1, i) br Next Else Response.Write 查询返回空结果 End If Else Response.Write 连接失败检查日志 End If Set db Nothing %这段代码覆盖了连接、查询、遍历、释放四个环节。如果页面输出正常说明类模块导入和连接配置都没问题。如果连接失败先看日志里记录的错误号和描述常见的是 Provider 未注册或认证失败。3.3 参数化查询的调用方式验证完基础连接后把业务查询改成参数化调用。以用户登录为例Dim cmd, params Set cmd New clsDbCommand Set cmd.Connection db.RawConnection params Array(userName, userPass) Dim affected affected cmd.ExecuteNonQuery( _ UPDATE users SET last_login GETDATE() WHERE name ? AND pass ?, _ params)注意这里的?占位符顺序要和params数组顺序一致。ADO 的参数绑定是按位置匹配的不是按名字。如果 SQL 里用了命名参数如name在adCmdText模式下仍然按位置绑定这一点和 SQL Server 原生 T-SQL 的行为不同容易踩坑。3.4 错误日志与排查入口资源里通常会带一个简单的日志函数把错误写入文本文件或数据库表。我一般会把它改成按天分文件避免单个日志文件过大。Public Sub LogError(ByVal funcName As String, _ ByVal errNum As Long, ByVal errDesc As String) Dim fso, ts, logPath logPath Server.MapPath(/logs/ado_error_ _ Year(Now) Month(Now) Day(Now) .log) Set fso CreateObject(Scripting.FileSystemObject) Set ts fso.OpenTextFile(logPath, 8, True) ts.WriteLine Now() | funcName | _ errNum | errDesc ts.Close Set ts Nothing Set fso Nothing End Sub日志路径要确保 IIS 或应用程序池账号有写入权限否则日志写不进去排查时就成了黑匣子。权限问题在 Windows 环境下比代码问题更常见尤其是部署到新服务器时。4. 避坑与排查ADO 封装里最容易翻车的五个点4.1 连接状态判断失效现象明明数据库正常Open()却返回 False日志里没有错误记录。原因ADODB.Connection.State在某些 Provider 下返回的不是adStateOpen而是其他值。另外如果连接字符串里指定的 Provider 和实际安装的版本不匹配Open会直接抛错但被On Error吞掉。解决在Open()里把Err.Number和Err.Description都写进日志不要只写一句“连接失败”。同时用m_Conn.Provider属性确认实际使用的 Provider 名称。4.2 参数顺序错位导致查询结果异常现象参数化查询不报错但返回的数据不对或者 UPDATE 影响了错误的行。原因ADO 在adCmdText模式下按位置绑定参数SQL 里?的顺序和Parameters.Append的顺序必须严格一致。如果中间插入了条件分支动态拼接 SQL顺序很容易乱。解决动态 SQL 场景下把参数收集到一个数组里最后统一 Append。不要边拼 SQL 边 Append 参数。4.3 记录集未关闭导致连接池耗尽现象站点运行一段时间后所有页面都卡住重启应用池后恢复。原因Recordset对象没有显式Close或者GetRows之后没有释放记录集。ADO 的连接池有上限记录集持有连接不释放池子很快见底。解决所有Execute返回的记录集用完立即Close并Set Nothing。GetRows之后记录集就可以关了数组和记录集之间没有依赖关系。4.4 超时设置不生效现象设置了CommandTimeout但慢查询还是卡很久。原因CommandTimeout只对Command对象生效如果直接用Connection.Execute用的是连接的CommandTimeout。另外某些 Provider 会忽略这个设置实际超时由数据库端控制。解决统一走Command对象执行查询不要混用Connection.Execute。同时在数据库端也设置查询超时作为兜底。4.5 类模块在页面间共享时的状态污染现象A 页面修改了连接字符串B 页面跟着变了。原因如果类模块被放在 Application 或 Session 作用域实例是共享的。连接字符串作为实例属性被修改后所有引用该实例的页面都会受影响。解决连接管理类不要放 Application 或 Session每次请求新建实例。连接池本身已经在底层做了复用不需要在应用层再共享连接对象。5. 进阶用法把 ADO2.20 Class 改造成带重试的连接层5.1 为什么需要重试数据库连接偶尔抖动是常态尤其是跨网段访问或数据库服务器在做维护时。一次Open失败就返回错误页面用户体验很差。给连接层加一个简单的重试逻辑能挡掉大部分瞬时故障。5.2 重试逻辑的实现在连接管理类里加一个OpenWithRetry方法Public Function OpenWithRetry(ByVal maxRetry As Long, _ ByVal intervalMs As Long) As Boolean Dim attempt As Long For attempt 1 To maxRetry If Me.Open() Then OpenWithRetry True Exit Function End If If attempt maxRetry Then 等待间隔后重试 Dim t As Single t Timer Do While Timer t intervalMs / 1000 DoEvents Loop End If Next attempt OpenWithRetry False End Function参数说明maxRetry建议设 3太多会拖长页面响应时间intervalMs建议 200 到 500 毫秒太短没意义太长用户等不及。DoEvents在 ASP 环境下行为不稳定如果是在纯 ASP 里用可以换成Server.ScriptTimeout配合简单的空循环或者直接去掉等待逻辑靠重试次数本身拉开时间间隔。5.3 重试的边界与验证重试不是万能的。认证失败、数据库不存在、SQL 语法错误这类问题重试多少次都一样。所以重试之前要先判断错误类型只对连接超时和网络类错误重试。验证方法很简单在测试环境里把数据库服务停掉观察页面是否在重试后返回友好提示而不是直接抛 500 错误。然后再把数据库启动确认下一次请求能正常恢复。错误类型是否重试判断依据连接超时是Err.Number 为 -2147467259认证失败否Err.Number 为 -2147217843数据库不存在否Err.Description 含 Cannot open database网络中断是Err.Number 为 -2147467259 且 Description 含 network这张表是我自己在项目里总结的不同 Provider 的错误号可能有差异落地时先用日志把实际错误号打出来再对照调整。5.4 一个我踩过的坑早期我把重试逻辑写在了业务层每个查询方法里都套一层循环结果代码重复不说还出现过重试期间连接对象被其他线程修改的情况。后来统一收到连接管理类里业务层只调OpenWithRetry世界就清净了。从那以后我每次封装数据库访问层都强制把重试、日志、超时这三件事放在最底层不让它们渗透到业务代码里。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Poppler Windows 24.07.0 运行时依赖与中文PDF解析实战指南

Poppler Windows 24.07.0 运行时依赖与中文PDF解析实战指南

简介:本资源为 Poppler for Windows 的预编译二进制安装包(v24.07.0),面向 Windows 平台 C/C 开发者、PDF 文档处理工具链构建者及需要快速集成 PDF 解析能力的工程人员,解决源码编译门槛高、依赖复杂、环境配置繁琐等…

2026/10/12 19:45:01 阅读更多 →
数字孪生与 Agent Harness 协同仿真:把 endpoint 改到 TaoToken 的联调大纲

数字孪生与 Agent Harness 协同仿真:把 endpoint 改到 TaoToken 的联调大纲

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

2026/10/12 19:44:00 阅读更多 →
Open Code 命令及使用技巧:把 settings 改到 TaoToken 的完整配置清单

Open Code 命令及使用技巧:把 settings 改到 TaoToken 的完整配置清单

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

2026/10/12 19:44:00 阅读更多 →

最新新闻

同花顺日线文件解析:从二进制乱码到可回测的OHLCV表

同花顺日线文件解析:从二进制乱码到可回测的OHLCV表

简介:这份资源面向金融数据分析初学者与量化爱好者,聚焦同花顺日线二进制文件(.day)的解析问题。压缩包仅4KB,内含2个文件:一个以600000为代码的日线样本文件,以及一份约20行的Python解析脚本&a…

2026/10/13 22:02:46 阅读更多 →
Axure RP 9 高效原型设计:动态面板、事件配置与母版管理实战指南

Axure RP 9 高效原型设计:动态面板、事件配置与母版管理实战指南

简介:《Axure RP 9 使用详解》docx文档面向产品经理、交互设计师及原型初学者,系统讲解从基础页面布局到动态面板、内联框架、函数等进阶交互功能,尤其针对项目投标时原型演示拿不出手的痛点,帮助读者做出专业、可演示的高保真原型…

2026/10/13 22:02:46 阅读更多 →
粒子群无功优化MATLAB实现:基于IEEE30节点的完整代码解析

粒子群无功优化MATLAB实现:基于IEEE30节点的完整代码解析

简介:针对电力系统网损偏高、电压质量不佳等运行优化需求,该资源提供基于粒子群算法的无功优化实现代码,采用IEEE30节点标准测试系统作为算例,适合电力专业研究生、科研人员与算法爱好者参考学习。代码完整覆盖粒子群寻优流程&…

2026/10/13 22:02:46 阅读更多 →
Spring Boot入门:自动配置与配置文件实战解析

Spring Boot入门:自动配置与配置文件实战解析

我最初接触 Spring Boot 时,最直观的感受就是"懒人福音"。这是 Spring 生态里少数能让我这种被 XML 配置折磨过的人,第一次体会到"开箱即用"的框架。如果你之前用传统 Spring 搭过项目,一定记得那些繁琐的配置&#xff1…

2026/10/13 22:02:46 阅读更多 →
FreeCAD中文翻译教程避坑指南:从参数化建模到动态数据驱动

FreeCAD中文翻译教程避坑指南:从参数化建模到动态数据驱动

简介:面向FreeCAD初学者的中文翻译版操作手册,它将官方英文文档wiki整理成更循序渐进的中文读本,帮助零基础用户快速理解参数化建模思路并掌握软件操作。FreeCAD是免费开源的跨平台3D建模软件,本手册涵盖安装、界面导航、3D视图、…

2026/10/13 22:02:46 阅读更多 →
基于SpringBoot的北京民宿推荐系统:内容算法与首都特色实践

基于SpringBoot的北京民宿推荐系统:内容算法与首都特色实践

1. 内容整体设计与思路拆解1.1 为什么是民宿推荐系统,而不是酒店预订系统先说个现象。很多人在做毕业设计时,一提到旅游住宿类系统,第一反应就是"酒店管理系统""民宿预订平台",但这类题目在答辩时很容易被打低…

2026/10/13 22:01:45 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/12 18:11:17 阅读更多 →