CursorAdapter + XMLAdapter:VFP数据交互双雄,打通内外数据任督二脉

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

FP开发者最头疼的事之一,就是数据在"里面"和"外面"之间来回折腾。CursorAdapter负责把外部数据源变成VFP临时表,XMLAdapter负责把VFP数据变成XML(或反过来)——两个类各司其职,组合起来却能打通任督二脉。

今天我们不讲大道理,直接上干货:这两个类到底怎么用,什么时候该用谁,怎么搭配效果最好。


一、CursorAdapter:让外部数据源变VFP临时表

1.1 核心定位

CursorAdapter是VFP9引入的数据访问类,它的核心使命就一句话:把任意数据源映射为VFP临时表

支持的数据源类型(DataSourceType属性):

DataSourceType

说明

DataSource属性值

"Native"

VFP本地表/临时表

被忽略

"ODBC"

ODBC数据源

有效的ODBC连接句柄

"ADO"

ADO RecordSet/Command

有效的ADO对象

"XML"

XML数据

被忽略

关键点:CursorAdapter打开的临时表始终是缓冲式临时表,默认开放式行缓冲。

1.2 五步上手CursorAdapter

*-- 第1步:创建CursorAdapter对象
LOCAL oCA
oCA = CREATEOBJECT("CursorAdapter")

*-- 第2步:设置数据源类型和连接
oCA.DataSourceType = "ODBC"
oCA.DataSource = SQLSTRINGCONNECT("Driver=SQL Server;Server=.;Database=Northwind;Uid=sa;Pwd=123")

*-- 第3步:设置查询命令
oCA.SelectCmd = "SELECT EmployeeID, LastName, FirstName FROM Employees"

*-- 第4步:设置可更新字段和主键(需要回写时)
oCA.Tables = "Employees"
oCA.KeyFieldList = "EmployeeID"
oCA.UpdatableFieldList = "LastName, FirstName"
oCA.UpdateNameList = "LastName Employees.LastName, FirstName Employees.FirstName"

*-- 第5步:填充临时表
oCA.CursorFill()
BROWSE

就这么简单。CursorFill执行后,VFP自动创建一个临时表,你可以像操作普通VFP表一样浏览、修改。

1.3 CursorFill的核心参数

oCA.CursorFill([lUseCursorSchema [, lNoData [, nOptions [, Source]]]])
  • lUseCursorSchema

    :.T.时使用CursorSchema属性定义的表结构创建临时表,.F.时自动推断

  • lNoData

    :.T.时只建结构不填数据——适合先搭框架再按需加载的场景

  • nOptions

    :对ADO是Command类型枚举值,对XML是XMLTOCURSOR()标记组合

  • Source

    :ADO场景下传入Command对象,VFP会自动解析参数

1.4 BeforeCursorFill:运行时动态改SQL

这是CursorAdapter最实用的功能之一——在执行查询前动态修改SelectCmd:

*-- 在BeforeCursorFill事件中动态添加WHERE条件
PROCEDURE oCA.BeforeCursorFill
LPARAMETERS lUseCursorSchema, lNoData, cSelectCmd
*-- 注意:修改的是参数cSelectCmd,不是属性SelectCmd
*-- 这样不会污染原始的SelectCmd属性
cSelectCmd = cSelectCmd + " WHERE LastName LIKE '" + THIS.cFilter + "%'"
*-- 不要返回.F.,否则CursorFill不会执行
ENDPROC

关键细节:BeforeCursorFill中修改cSelectCmd参数不会改变SelectCmd属性值,CursorRefresh也使用参数值而非属性值。这意味着你可以在属性中保存基础SQL,在事件中按需追加条件。

1.5 CursorAttach:把已有临时表纳入管理

不是所有临时表都需要从零创建。如果已经有了(比如SQL Pass-Through的结果),CursorAttach可以把它"挂"到CursorAdapter上:

*-- 先用SPT获取数据
lnHandle = SQLSTRINGCONNECT(cConnStr)
SQLEXEC(lnHandle, "SELECT * FROM Orders", "tmpOrders")

*-- 再挂到CursorAdapter
oCA = CREATEOBJECT("CursorAdapter")
oCA.DataSourceType = "ODBC"
oCA.DataSource = lnHandle
oCA.Tables = "Orders"
oCA.KeyFieldList = "OrderID"
oCA.UpdatableFieldList = "CustomerID, OrderDate"
oCA.CursorAttach("tmpOrders", .T.)  && .T.继承临时表已有属性

注意:一个临时表同一时间只能附属一个CursorAdapter实例。要先换"主人",得先CursorDetach。

1.6 数据更新回写

CursorAdapter最大的价值不是读数据,而是改了就能回写

*-- 修改临时表数据后,调用TABLEUPDATE
TABLEUPDATE(.T., .T., oCA.Alias)

*-- 或者用CursorAdapter的Save方法(框架封装)
oCA.Save()

底层原理:CursorAdapter通过Tables、KeyFieldList、UpdatableFieldList、UpdateNameList四个属性构建UPDATE/INSERT/DELETE语句,调用TABLEUPDATE时自动执行。


二、XMLAdapter:XML与VFP数据的双向桥梁

2.1 核心定位

如果说CursorAdapter是"把外部数据拉进来",XMLAdapter就是"把VFP数据推出去(或拉回来)"的双向转换器。

核心能力:
LoadXML:从XML文件或字符串加载到Tables集合
ToXML:从VFP临时表生成XML文件或字符串
ToCursor:从XMLTable生成VFP临时表
ApplyDiffgram:将DiffGram变更应用到VFP表

2.2 从XML创建VFP临时表

LOCAL oXA AS XMLAdapter
oXA = CREATEOBJECT("XMLAdapter")

*-- 从文件加载XML
oXA.LoadXML("c:\data\employees.xml", .T.)  && .T.表示是文件

*-- 查看加载了多少表
? oXA.Tables.Count&& 有几个XMLTable对象

*-- 将第一个表转为VFP临时表
oXA.Tables.Item(1).ToCursor(, "xmlEmployees")
BROWSE

2.3 从VFP临时表生成XML

LOCAL oXA AS XMLAdapter
oXA = CREATEOBJECT("XMLAdapter")

*-- 添加临时表到XMLAdapter
USE employees IN 0 ALIAS emp
oXA.AddTableSchema("emp")

*-- 生成XML文件
oXA.ToXML("c:\output\employees.xml", , .T.)  && .T.表示输出到文件

2.4 DiffGram:变更追踪与回写

DiffGram是XMLAdapter的杀手级功能——它不只记录数据,还记录变更前后的差异

*-- 生成包含变更的DiffGram
oXA.IsDiffGram = .T.
oXA.ToXML(@cXML, , .F., .T., .T.)  
*-- 第4个参数.T.:包含before部分
*-- 第5个参数.T.:只输出变更

*-- 应用DiffGram回写变更
oXA2 = CREATEOBJECT("XMLAdapter")
oXA2.LoadXML(cXML)
oXA2.Tables.Item(1).ApplyDiffgram()

ApplyDiffgram甚至可以配合CursorAdapter使用——传入oCursorAdapter参数,让更新走CursorAdapter的连接回写到远程数据库!

*-- 用CursorAdapter作为更新通道
oXA2.Tables.Item(1).ApplyDiffgram(, oCA, .T.)

2.5 嵌套表处理

XMLAdapter对嵌套表的处理因数据源而异:

数据源

处理方式

ADO.NET DataSet

每个嵌套表作为独立XMLTable,可分别ToCursor

SQL XML

所有嵌套表合并为单个结果,只有顶层父表在Tables集合中

RespectNesting属性:设为.T.时,LoadXML保留XML层次结构,ToXML生成嵌套XML。


三、双雄合璧:CursorAdapter + XMLAdapter实战场景

3.1 场景一:BS模式下JSON转CursorAdapter

祺佑框架的DAL_CA类提供了parsejson方法,底层正是CursorAdapter + XML/JSON转换:

*-- 从WebAPI获取JSON,转为CursorAdapter管理的临时表
oDALCA = NEWOBJECT("DAL_Employees", "dal_employees.prg")
oDALCA.CursorFill(.T.)  && 用CursorSchema填充

*-- 修改数据
oDALCA.Add()        && 新增空行
REPLACE LastName WITH '中国' IN (oDALCA.Alias)

*-- 检查、保存
? oDALCA.detectchanged()  && .T.表示有变更
IF !oDALCA.Save()
WAIT WINDOW oDALCA.msg  && 显示错误原因
ENDIF

*-- 撤销
? oDALCA.Undo()     && 撤销所有操作

3.2 场景二:XML中间层数据交换

WebAPI返回XML数据,用XMLAdapter解析后挂到CursorAdapter上管理:

*-- 1. XMLAdapter加载XML
oXA = CREATEOBJECT("XMLAdapter")
oXA.LoadXML(cWebResponse)

*-- 2. 转为临时表
oXA.Tables.Item(1).ToCursor(, "tmpData")

*-- 3. 用CursorAttach纳入CursorAdapter管理
oCA = CREATEOBJECT("CursorAdapter")
oCA.DataSourceType = "Native"
oCA.Tables = "MyTable"
oCA.KeyFieldList = "ID"
oCA.UpdatableFieldList = "Name, Value"
oCA.CursorAttach("tmpData")

*-- 4. 修改后回写
*-- ... 修改数据 ...
TABLEUPDATE(.T., .T., "tmpData")

3.3 场景三:离线编辑 + DiffGram同步

这是最经典的组合用法:

*-- 1. 从远程拉数据
oCA = CREATEOBJECT("CursorAdapter")
oCA.DataSourceType = "ODBC"
oCA.DataSource = lnHandle
oCA.SelectCmd = "SELECT * FROM Products"
oCA.CursorFill()

*-- 2. 生成XML(带Schema),保存到本地
oXA = CREATEOBJECT("XMLAdapter")
oXA.AddTableSchema(oCA.Alias)
oXA.ToXML("c:\offline\products.xml", , .T.)

*-- 3. 离线编辑...(可能在另一台机器上)
oXA2 = CREATEOBJECT("XMLAdapter")
oXA2.LoadXML("c:\offline\products.xml")
oXA2.Tables.Item(1).ToCursor(, "Products")
*-- ... 用户修改数据 ...

*-- 4. 生成DiffGram
oXA3 = CREATEOBJECT("XMLAdapter")
oXA3.IsDiffGram = .T.
oXA3.AddTableSchema("Products")
oXA3.ToXML(@cDiffGram, , .F., .T., .T.)

*-- 5. 回到在线环境,用ApplyDiffgram + CursorAdapter回写
oXA4 = CREATEOBJECT("XMLAdapter")
oXA4.LoadXML(cDiffGram)
oXA4.Tables.Item(1).ApplyDiffgram(, oCA, .T.)

四、CursorAdapter关键属性速查

属性

说明

DataSourceType

数据源类型:Native/ODBC/ADO/XML

DataSource

数据源对象(ODBC句柄/ADO RecordSet)

SelectCmd

查询命令(SQL SELECT语句)

CursorSchema

临时表结构定义

Tables

对应的基表名列表

KeyFieldList

主键字段列表

UpdatableFieldList

可更新字段列表

UpdateNameList

字段到基表列的映射

AllowInsert/AllowUpdate/AllowDelete

是否允许对应操作

FetchMemo

是否获取备注字段

BufferModeOverride

缓冲模式覆盖

五、XMLAdapter关键属性速查

属性

说明

IsDiffGram

是否以DiffGram格式处理

RespectNesting

是否保留XML嵌套层次

PreserveWhiteSpace

是否保留XML中的空格

UTF8Encoded

是否UTF-8编码

RespectCursorCP

是否尊重游标代码页

XMLSchemaLocation

外部Schema文件位置

XMLNameIsXPath

XMLName是否为XPath表达式

UseCodePage

是否使用代码页

六、避坑指南

6.1 CursorAdapter常见陷阱

  1. 错误2112

    :临时表已附属另一个CursorAdapter——必须先CursorDetach再CursorAttach

  2. 不能使用本地/远程视图作为Native源

    ——但可以用CursorAttach挂上

  3. CursorFill会关闭当前已附属的临时表

    ——要保留的话先CursorDetach

  4. BeforeCursorFill返回.F.会阻止CursorFill执行

    ——但当前临时表不会被关闭

6.2 XMLAdapter常见陷阱

  1. XDR Schema必须转为XSD

    ——需修改XMLNamespace属性,否则XML解析器无法重新加载

  2. 数值溢出(.

    )的XML会导致加载失败**——MSXML解析器会报错

  3. XMLNameIsXPath为.T.且XMLName不为空时,ToXML会失败
  4. ApplyDiffgram只为Tables集合中的XMLTable支持

    ——嵌套在链中的子表不支持

  5. UseCodePage和UTF8Encoded的组合会影响编码结果

    ——多表场景确保代码页一致


七、总结

维度

CursorAdapter

XMLAdapter

核心用途

外部数据源→VFP临时表

VFP数据↔XML双向转换

数据源

ODBC/ADO/Native/XML

XML文件/字符串

更新回写

自动构建UPDATE/INSERT/DELETE

DiffGram追踪变更

最佳场景

在线CRUD操作

离线同步、跨系统数据交换

框架应用

DAL_CA派生类

XML中间层、WebAPI交互

一句话:CursorAdapter是"住"在数据源旁边的管家,XMLAdapter是"跑"在数据路上的快递员。管家管日常,快递跑远程——配合使用,VFP的数据交互能力远超你想象。

AI 时代程序员必备技能

Codex、Claude Code、Cursor、Hermes Agent、OpenClaw等工程化实战专栏 ,讲透 AI 如何接管脏活累活

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值