VFP CursorAdapter 起步二(作者:Doug Hennig 譯者:fbilo)

用 CursorAdapter 來取得和更新數據

在 VFP8 中新增的 CursorAdapter 基類提供一個統一、易用的數據接口。Doug Hennig 在這個月的文章中演示了怎樣使用 CursorAdapter 來訪問本地數據和 ODBC、ADO和XML這樣的遠程數據——討論了使用各種數據源相應的特殊要求和實現途徑。
正文:
如我在上一篇文章中所提到的那樣,在VFP8中一個最重要的、也是最精彩的新功能是新的 CursorAdapter 基類。在那篇文章中,我們研究了一下 CursorAdapter 的屬性、事件和方法,並討論了它相對於遠程視圖、SQL PassThrough(SPT)、ADO和XML的優勢。
在開始使用 CursorAdapter 之前,你需要根據要訪問的是本地數據還是通過ODBC、ADO或者XML的遠程數據源的不同,注意這個類所相應的不同的特殊要求。這個月的文章就講述了使用各種數據源的細節。
使用本地數據源
×××××××
儘管我們很清楚 CursorAdapter 是試圖用來標準化和簡化對非VFP數據的訪問方式的,不過你還是可以把它當作是 Cursor 的代替品用它來訪問VFP數據:只要把它的 DataSourceType 屬性設置成 "Native"。爲什麼要這麼做呢?因爲你的應用程序將來可能會需要升遷——那時候你就可以把 DataSourceType 屬性設置成其它幾個選項之一(當然可能還需要修改其它幾個屬性,例如設置連接信息等等),就能輕鬆的切換到另一種數據庫引擎,例如SQL Server。
當 DataSourceType 屬性的設置爲 "Native" 的時候,VFP會忽略它的 DataSource屬性。SelectCmd屬性必須是一個 SQL Select 語句(而不是一個 USE 命令或表達式),這就意味着你用 CursorAdapter 不是直接操作本地表而是操作一個類似於本地視圖那樣的東西。你還必須確保VFP能夠找到出現在那個 Select 語句中的任何表,因此,如果這些表不在當前路徑中,那麼你就需要設置一下路徑或者打開這些表所屬的數據庫。此外,就跟用視圖一樣,如果你想讓這個 Cursor 是可更新的,你還必須設置好那些與更新相關的屬性(KeyFieldList、Tables、UpdatableFieldlist和 UpdateNameList)。
下面的例子(文章附件 NativeExample.prg)會用 VFP 示例數據庫中的 Customer 表建立一個可更新的 Cursor:
local loCursor as CursorAdapter, laErrors[1]
Open database (_samples + \'data\\testdata\')
with loCursor
.Alias = \'customercursor\'
.DataSourceType = \'Native\'
.SelectCmd = "Select CUST_ID, COMPANY, CONTACT FROM CUSTOMER " + ;
"WHERE COUNTRY = \'Brazil\'"
.KeyFieldList = \'CUST_ID\'
.Tables = \'CUSTOMER\'
.UpdatableFieldList = \'CUST_ID, COMPANY, CONTACT\'
.UpdateNamelist = \'CUST_ID CUSTOMER.CUST_ID, \'+ ;
\'COMPANY CUSTOMER.COMPANY, CONTACT CUSTOMER.CONTACT\'
if .CursorFill()
browse
tableupdate(1)
else
aerror(laErrors)
messagebox(laErrors[2])
endif .CursorFill()
endwith
close databases all
使用 ODBC
×××××
ODBC 是 DataSourceType 屬性四種設置中最簡單的一種。把 DataSource 設置爲一個打開了的 ODBC 連接句柄、設置一下常用的屬性、然後調用 CursorFill 來取得數據。如果你設好了 KeyFieldList、Tables、UpdatableFieldList和 UpdateNameList屬性,VFP 會自動把你對數據的任何改動轉換成相應的 UPDATE、INSERT、和 DELETE 語句來把改動提交到後臺數據源。如果你想用的是一個存儲過程,那麼要相應的設置 *Cmd、*CmdDataSource和 *CmdDataSourceType屬性(* 代表 “Delete”、“Insert”或“Update”)。
這裏是附件 ODBCExample.prg 中的一個例子,它調用 Sql Server 自帶的 NorthWind 數據庫中的 CustOrderHist 存儲過程來取得銷售給某個客戶的單位產品總數。
local lcConnString, loCursor as CursorAdapter, laErrors[1]
lcConnString = \'driver=SQL Server;server=(local);database=Northwind;uid=sa;pwd=;"+ ;
"trusted_connection=no\'
** 把上面連接字符串中的密碼改成你的SQL Server 登錄的密碼
loCursor = createobject(\'CursorAdapter\')
with loCursor
.Alias = \'Customerhistory\'
.DataSourceType = \'ODBC\'
.DataSource = SQLStringConnect(lcConnString)
.SelectCmd = "exec CustOrderhist \'ALFKI\'"
if .CursorFill()
browse
else
aerror(laErrors)
messagebox(laErrors[2])
endif .CursorFill()
endwith
使用 ADO
××××
與使用 ODBC 相比,使用 ADO要多一些需要操心的事情:
×× DataSource 必須被設置成一個 ADO RecordSet,而且這個 RecordSet 的 ActiveConnection 屬性需要被設置成一個打開了的 ADO Connection 對象。
××如果你想要使用一個參數化查詢(與下載全部數據相比,這可能是更常用的方式),你必須把一個 ADO Command 對象作爲第四個參數傳遞給 CursorFill 方法,而且這個 Command 對象的 ActiveConnection 屬性需要被設置成一個打開了的 ADO Connection 對象。VFP會爲你照顧好填充 Command對象的參數化集合的事情(它通過分析 SelectCmd 來找出參數),不過參數所包含的值當然還是必須在有效取值範圍內的。
××在數據環境中只有一個使用了 ADO 的 CursorAdapter 這樣的情況是比較簡單的:如果需要的話,你可以把 UseDEDataSource 屬性設置成 .T.,然後根據你的需要把數據環境的 DataSource 和 DataSourceType 屬性設置成 CursorAdapter。不過,如果數據環境中有多個 CursorAdapter 的話,這種辦法就無效了。原因是 DataEnvironment.DataSource 所引用的 ADO RecordSet 只能包含一個 CursorAdapter 的數據;當你爲第二個 CursorAdapter 調用 CursorFill 方法的時候,會出現“RecordSet is already open (RecordSet 記錄集已經打開)”的錯誤。所以,如果你的數據環境中有超過一個的 CursorAdapter,你必須要把 UseDEDataSource 設置成 .F.,並自行管理每個 CursorAdapter 的 DataSource 和 DataSourceType 屬性(或者你可以使用一個能夠管理這種情況的 DataEnvironment 的子類)。
附件 ADOExample.prg 中的示例代碼演示了怎樣藉助一個 ADO Command 對象來取得數據。這個示例還演示了使用 VFP8 中新的結構化錯誤處理的功能。對 ADO Connection 對象的 Open 方法的調用被封裝在一個 TRY...CATCH...ENDTRY 語句中,以捕捉調用這個方法失敗的時候將會出現的 COM 錯誤。
local loConn as ADODB.Connection, ;
loCommand as ADODB.Command, ;
loException as Exception, ;
loCursor as CursorAdapter, ;
lcCountry, ;
laErrors[1]
loConn = createobject(\'ADODB.Connection\')
with loConn
.ConnectionString = \'provider=SQLOLEDB.1;data source=(local);\' + ;
\'initial catalog=Northwind;uid=sa;pwd=dhennig;trusted_connection=no\'
&& 把上面連接字符串中的密碼改成你的SQL Server 登錄的密碼
try
.Open()
catch to loException
messagebox(loException.Message)
cancel
endtry
endwith
loCommand = createobject(\'ADODB.Command\')
loCursor = createobject(\'CursorAdapter\')
with loCursor
.Alias = \'Customers\'
.DataSourceType = \'ADO\'
.DataSource = createobject(\'ADODB.RecordSet\')
.SelectCmd = \'select * from customers where country=?lcCountry\'
lcCountry = \'Brazil\'
.DataSource.ActiveConnection = loConn
loCommand.ActiveConnection = loConn
if .CursorFill(.F., .F., 0, loCommand)
browse
else
aerror(laErrors)
messagebox(laErrors[2])
endif .CursorFill(.F., .F., 0, loCommand)
endwith
使用 XML
××××
用 CursorAdapter 來操作 XML 需要一些特殊的設置。下面是這些問題:
×× DataSource 屬性被忽略;
×× CursorSchema 屬性必須被填充好——即使你給 CursorFill 傳遞的第一個參數是 .F. 也一樣——否則將會出錯。
×× SelectCmd 必須被設置成一個表達式,例如一個用戶自定義函數(UDF)或者對象方法名,該表達式能夠爲 Cursor 返回 XML。
××對 Cursor 的改動會被轉換成一個 DiffGram,它是“包含着被改動了的字段或者記錄,在被改動之前、被改動之後的值”的XML,當需要更新的時候,它被放在 DiffGram 屬性中。
××爲了把數據更動回寫到數據源中去,UpdateCmdDataSourceType屬性必須被設置爲“XML”,並且 UpdateCmd 必須被設置成一個能夠處理提交更新任務的表達式(象前面一樣,這個表達式也是象一個 UDF 或者對象的方法)。你可能會需要把“This.DiffGram”傳遞給那個 UDF,這樣它就可以把更新提交給後臺數據源。
這個 Cursor 所使用的 XML源文件可能來自各種不同的地方。例如,你可以調用這樣一個UDF:它能用 CursorToXML()來把一個VFP Cursor 轉換成 XML,並返回結果:
use CUSTOMERS
cursortoxml(\'customers\', \'lcXML\', 1, 8, 0, \'1\')
Return lcXML
UDF 可以調用一個 Web Service,這個 Web Service 則返回一個 XML 結果集。這裏是一個例子,我建立了一個 Web Service 並註冊在我自己的系統上,而智能感知則爲我生成了下面的代碼(具體的細節並不重要,它只是演示了一個 Web Service 的例子):
loWS = newobject("WSclient\', home() + \'ffc\\_webservices.vcx\')
loWS.cWSName = \'dataserver web service\'
loWS = loWS.SetupClient(\'http://localhost/' + ;
\'SQDataServer/dataserver.WSDL\', \'dataserver\', ;
\'dataserverSoapPort\')
lcXML = loWS.GetCustomers()
Return lcXML
它能夠在一個 Web Server 上使用 SQLXML 3.0 去執行一個存儲在一個臨時文件中的 SQL Server 2000 查詢(要了解關於 SQLXML 更多的信息,請訪問 http://msdn.microsoft.com並查找 SQLXML)。下面的代碼使用一個 MSXML2.XMLHTTP 對象通過 HTTP 從 Northwind數據庫的 Customers表來取得所有的記錄,稍後我們將做更進一步的解釋。
local loXML as MSXML2.XMLHTTP
loXML = createobject(\'MSXML2.XMLHTTP\')
loXML.open(\'POST\', \'http://localhost/northwind/' + ;
\'template/getallcustomers.xml, .F.)
loXML.setRequestHeader(\'Content-type\', \'text/xml\')
loXML.send()
return loXML.responseText
處理更新的事情要更復雜一點。數據源必須或者能夠接受並處理一個 DiffGram (比如 SQL Server 2000 的情況),或者你必須自己去弄清楚所有的改動、執行一系列的 SQL 語句(UPDATE、INSERT和 DELETE)去提交更新。
這裏是個使用了帶 XML 數據源的 CursorAdapter 的例子(XMLExample.prg)。要注意的是:SelectCMD和 UpdateCMD都是要調用 UDF 的。在 SelectCMD 的情況中,要返回數據的客戶編號被傳遞給一個叫做 GetNEWustomers 的 UDF,這個我們稍後再提。在 UpdateCmd 的情況中,VFP把 DiffGram 屬性傳遞給 SendNWXML,這個我們也稍後再提。
local loCustomers as CursorAdapter, ;
laErrors[1]
loCustomers = createobject(\'CursorAdapter\')
with loCustomers
.Alias = \'Customers\'
.CursorSchema = \'CUSTOMERID C(5), COMPANYNAME C(40), \' + ;
\'CONTACTNAME C(30), CONTACTTITLE C(30), ADDRESS C(60), \' + ;
\'CITY C(15), REGION C(15), POSTALCODE C(10), COUNTRY C(15), \' + ;
\'PHONE C(24), FAX C(24)\'
.DataSourceType = \'XML\'
.KeyFieldList = \'CUSTOMERID\'
.SelectCmd = \'GetNWCustomers([ALFKI])\'
.Tables = \'CUSTOMERS\'
.UpdatableFieldList = \'CUSTOMERID, COMPANYNAME, CONTACTNAME, \' + ;
\'CONTACTTITLE, ADDRESS, CITY, REGION, POSTALCODE, COUNTRY, PHONE, FAX\'
.UpdateCmdDataSourceType = \'XML\'
.UpdateCmd = \'SendNWXML(This.DiffGram)\'
.UpdateNameList = \'CUSTOMERID CUSTOMERS.CUSTOMERID, \' + ;
\'COMPANYNAME CUSTOMERS.COMPANYNAME, \' + ;
\'CONTACTNAME CUSTOMERS.CONTACTNAME, \' + ;
\'CONTACTTITLE CUSTOMERS.CONTACTTITLE, \' + ;
\'ADDRESS CUSTOMERS.ADDRESS, \' + ;
\'CITY CUSTOMERS.CITY, \' + ;
\'REGION CUSTOMERS.REGION, \' + ;
\'POSTALCODE CUSTOMERS.POSTALCODE, \' + ;
\'COUNTRY CUSTOMERS.COUNTRY, \' + ;
\'PHONE CUSTOMERS.PHONE, \' + ;
\'FAX CUSTOMERS.FAX\'
if .CursorFill(.T.)
browse
else
aerror(laErrors)
messagebox(laErrors[2])
endif .CursorFill(.T.)
endwith
這裏是 GetNWCustomers 的代碼。它使用了一個 MSXML2.XMLHTTP 對象來訪問一個位於一個Web Server 上的名叫 CustomersByID.xml 的 SQL Server 2000 XML 模板,並返回結果。要獲取數據的 Customer ID 被作爲一個參數傳遞給這段代碼:
lparameters tcCustID
local loXML as MSXML2.XMLHTTP
loXML = createobject(\'MSXML2.XMLHTTP\')
loXML.open(\'POST\', "http://localhost/northwind/template/customersbyid.xml?";; + ;
"customerid=" + tcCustID, .F.)
loXML.setRequestHeader(\'Content-type\', \'text/xml\')
loXML.send()
return loXML.responseText
這段代碼裏引用的名爲 CustomersByID.XML 的 XML 模板的內容如下:
SELECT *
FROM Customers
WHERE CustomerID = @customerid
FOR XML AUTO
把這個文件放在用於 Northwind 數據庫的一個虛擬目錄中(參見補充文檔《設置 SQL Server 2000 XML 訪問》以瞭解更多關於爲 SQL Server 2000 設置 IIS 的內容、以及這篇文章所需要的特殊細節。)
SendNWXML 的內容看起來與 GetNWCustomers類似,除了它接收的參數是一個 DiffGram,然後它把這個 DiffGram 加載到一個 MSXML2.DOMDocumnet 對象中,並把這個對象傳遞給 Web Server,該 Web Server 會通過 SQLXML把這個對象傳遞給 SQL Server 2000 去處理。
lparameters tcDiffGram
local loDOM as MSXML2.DOMDocument, ;
loXML as MSXML2.XMLHTTP
loDOM = createobject(\'MSXML2.DOMDocument\')
loDOM.async = .F.
loDOM.loadXML(tcDiffGram)
loXML = createobject(\'MSXML2.XMLHTTP\')
loXML.open(\'POST\', \'http://localhost/northwind/', .F.)
loXML.setRequestHeader(\'Content-type\', \'text/xml\')
loXML.send(loDOM)
運行 XMLExample.prg 來看看它是怎麼工作的。你將會在 Browse 窗口中看到一臺記錄(客戶 ALFKI)。試着改動幾個字段的值,然後關閉這個窗口,再運行 PRG 一遍。你會看到你的改動已經被寫入到後臺數據源中了。
總結
××
儘管 CursorAdapter 基類提供了一種對遠程數據源的統一的結構,而不管你使用的是 ODBC、ADO還是XML——但是,根據你選擇的數據訪問機制的不同,對 CursorAdapter 的設置也有一些區別。這些區別取決於數據訪問機制的本身。
下個月,我將通過建立一些可重用的數據類、並討論怎樣在報表中使用 CursorAdapter 來結束這個系列的專題。
補充文檔:
《設置 SQL Server 2000 XML 訪問》
爲了能夠在一個瀏覽器或者其它 HTTP 客戶端用一個 URL來訪問 SQL Server 2000,你需要做一些工作。首先,你需要從 MSDN 網站(http://msdn.microsoft.com——查詢一下“SQLXML”,然後選擇下載)去下載和安裝 SQLXML 3.0。
接着,你需要設置一個 IIS 虛擬目錄。步驟如下:從開始菜單|程序|SQLXML 3.0文件夾中單擊“Configure IIS Support(設置 IIS 支持)”。展開你的服務器節點,選擇要使用的 Web 站點,然後單擊鼠標右鍵,選擇“新建|虛擬目錄”,在出現的對話框的“常規”頁中輸入虛擬目錄的名稱和它的物理路徑。在這裏,我們使用“Northwind”作爲虛擬目錄名、“NorthwindTemplates”作爲物理路徑。使用 Windows 資源管理器在你的系統上的什麼地方建立這個物理目錄,然後給它建一個名爲“Template”的子目錄(稍後我們將會用到這個子目錄)。把附件中的兩個模板文件 GetAllCustomers.xml 和 CustomersByID.xml 拷貝到這個子目錄中。
在“安全”頁中,輸入訪問 SQL Server 的相應的信息,例如用戶名和密碼或者你想採用的特定的驗證機制。在“數據源”頁上,選擇 SQL Server,如果需要的話,還要選擇要使用的數據庫。在這裏我們選擇 Northwind 數據庫。在“設置”頁上選擇希望的設置,至少要選上“允許模板查詢”和“允許 Post”。
在“虛擬名稱”頁中,從類型組合框中選擇“模板”,並輸入一個虛擬名稱(在這裏我們使用“template”)和物理路徑(它應該是虛擬目錄的一個子目錄,在這裏就是 "Template"子目錄),這是使用模板的需要。好,單擊“確定”。
現在我們測試一下是否每樣東西都設置正確了,我們將通過使用你拷貝到 Template 子目錄中去得 GetAllCustomers.xml來訪問 SQL Server。它的內容如下:

<root xmlns:sql="urn:schemas-microsoft-com:xml-sql">
<sql:query client-side-xml="0">
SELECT *
FROM Customers
FOR XML AUTO
</sql:query>
</root>

爲了測試的目的,打開你的瀏覽器,並輸入這個URL:http://localhost/northwind/template/getallcustomers.xml,你就會在瀏覽器中看到XML形式的 Northwind Customers 表的內容了。

<root xmlns:sql="urn:schemas-microsoft-com:xml-sql">
<Customers CustomerID="ALFKI" CompanyName="Alfreds
Futterkiste" ContactName="Maria Anders"
ContactTitle="Sales Representative"
Address="Obere Str. 57" City="Berlin" PostalCode="12209"
Country="Germany" Phone="030-0074321"
Fax="999-999-9999" />
現在可以運行本文中的SQLXML示例了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章