VFP的數據策略:基礎篇

VFP的數據策略:基礎篇

作者:Doug Hennig  翻譯:老瓷

概述

在VFP應用程序中,有很多方法可以訪問非VFP數據(如SQL Server):
遠程視圖、SQL Passthrough(SPT--譯者著)、ADO、XML……本文件將探討不同機制的利弊,並討論何時適合使用特定策略。我們還將研究VFP中一種令人興奮的新技術CursorAdapter,它將使訪問遠程數據比早期版本更容易。

介紹

越來越多的VFP開發人員將數據存儲在VFP表以外的其他地方,如SQL Server或Oracle。這有很多原因,包括VFP表的脆弱性(感知上和實際上)、安全性、數據庫大小和公司標準。微軟在每一個版本中都使非VFP數據的訪問變得更加容易,甚至還鼓勵它在VFP7 CD中加入MSDE(微軟數據引擎,一個免費的、精簡版的SQL Server)。
然而,訪問後端數據庫從未像使用VFP表那樣容易。此外,還可使用多種機制來執行此操作:

  • 基於ODBC連接的遠程視圖。
  • SQL Passthrough(SPT)函數,如SQLCONNECT()、SQLEXEC()和SQLDISCONNECT(),它們也基於ODBC連接。
  • ActiveX數據對象(ADO),爲數據庫引擎的OLE DB提供程序提供面向對象的前端。
  • XML,這是一種輕量級、獨立於平臺的數據傳輸機制。

你應該選擇哪種機制?答案(正如大多數VFP問題的答案)是“取決於”。它取決於許多因素,包括開發團隊的經驗和專業知識、基礎設施、應用程序的需求、預期的未來需求等等。
讓我們來看看這些機制中的每一種,包括它們的優點和缺點,需要注意的一些技術,以及我對每一種機制在整體方案中的位置的看法。我們還將瞭解我認爲VFP 8中最大的新特性之一CursorAdapter類,以及它如何使通過ODBC、ADO或XML進行遠程數據訪問變得更加容易和一致。

遠程視圖

與本地視圖一樣,遠程視圖只是在數據庫容器中定義的預定義SQL SELECT語句。不同之處在於,遠程視圖通過ODBC(即使它訪問的數據是VFP)而不是本機訪問數據。
可以使用create SQL view命令以編程方式創建遠程視圖,也可以使用視圖設計器以可視化方式創建遠程視圖。在這兩種情況下,都需要指定要使用的ODBC連接。連接可以是在系統上設置的ODBC數據源(DSN),也可以是已在同一數據庫中定義的連接對象。下面是一個示例,它創建了SQL Server附帶的示例Northwind數據庫的Customers表的遠程視圖(我們將在示例中廣泛使用該數據庫)。這是從GENDBC生成的代碼中摘錄的;實際上還有很多代碼可以設置連接、視圖和字段的各種屬性。

CREATE CONNECTION NORTHWINDCONNECTION ;  
   CONNSTRING "DSN=Northwind SQL; UID=sa; PWD=testdb; " + ; 
   "DATABASE=Northwind; TRUSTED_CONNECTION=No" 
CREATE SQL VIEW "CUSTOMERSVIEW" ;  
   REMOTE CONNECT "NorthwindConnection" ;  
   AS SELECT * FROM dbo.Customers Customers 
DBSetProp('CUSTOMERSVIEW', 'View', 'UpdateType', 1) 
DBSetProp('CUSTOMERSVIEW', 'View', 'WhereType', 3) 
DBSetProp('CUSTOMERSVIEW', 'View', 'FetchMemo', .T.) 
DBSetProp('CUSTOMERSVIEW', 'View', 'SendUpdates', .T.) 
DBSetProp('CUSTOMERSVIEW', 'View', 'Tables', 'dbo.Customers') 
DBSetProp('CUSTOMERSVIEW.customerid', 'Field', 'KeyField', .T.) 
DBSetProp('CUSTOMERSVIEW.customerid', 'Field', 'Updatable', .F.) 
DBSetProp('CUSTOMERSVIEW.customerid', 'Field', 'UpdateName', ; 
   'dbo.Customers.CustomerID') 
DBSetProp('CUSTOMERSVIEW.companyname', 'Field', 'Updatable', .T.) 
DBSetProp('CUSTOMERSVIEW.companyname', 'Field', 'UpdateName', ; 
   'dbo.Customers.CompanyName')

可以升遷現有應用程序的最簡單方法之一是使用升遷嚮導創建VFP表的SQL Server版本,然後創建新數據庫(例如REMOTE.DBC),並在該數據庫中創建與它們所基於的表同名的遠程視圖。這樣,打開遠程視圖的代碼將與打開本地表的代碼完全相同,只是您將首先打開其他數據庫。例如:

if oApp.lUseLocalData 
  open database Local 
else 
  open database Remote 
endif 
use CUSTOMERS

如果在窗體和報表的數據環境中使用遊標對象,則需要做一些額外的工作,因爲這些對象引用了將視圖拖放到數據環境中時選擇的特定數據庫。要處理此問題,請將類似於以下內容的代碼放入DataEnvironment的BeforeOpenTables方法中:

local loObject 
for each loObject in This.Objects 
  if upper(loObject.BaseClass) = 'CURSOR' and not empty(loObject.Database) 
    loObject.Database = iif(oApp.lUseLocalData, 'local.dbc', 'remote.dbc') 
  endif upper(loObject.BaseClass) = 'CURSOR' .. 
next loObject

需要注意的一點是:當您打開一個視圖時,VFP會嘗試在DBC中鎖定該視圖的記錄,即使它只是短暫的。這可能會在繁忙的應用程序中引起爭用,在這些應用程序中,多個用戶可能會嘗試同時打開窗體。儘管有一些解決方法(將DBC複製到本地工作站並使用該方法,或者在VFP 7和更高版本中,使用SET reproces SYSTEM來增加鎖爭用的超時),但這是需要計劃的。
關於偏僻的景色是不是一件好事,有很大的爭議。如果您有興趣閱讀有關論點各方面的內容,請查看以下鏈接:
http://fox.wikis.com/wc.dll?Wiki~RemoteViews~VFP
http://fox.wikis.com/wc.dll?Wiki~MoreOnRemoteViews~VFP
http://fox.wikis.com/wc.dll?Wiki~Client/ServerDataAccessTechniques~VFP
http://fox.wikis.com/wc.dll?Wiki~Client/ServerTechniquesPerformance~VFP

優勢

遠程視圖的優點是:

  • 您可以使用視圖設計器可視化地創建遠程視圖。好吧,在VFP 8解決了視圖設計器的許多已知問題(尤其是涉及兩個以上表的複雜視圖)之前,這不一定是一個優勢,但即使在早期版本中,也可以非常快速和輕鬆地創建簡單視圖。直觀地看到底層表中的所有字段,使用友好的界面輕鬆地設置SQL SELECT語句的各個部分,並使用複選框或其他UI元素快速設置視圖的屬性,這是非常好的。
  • 從語言的角度來看,遠程視圖就像表一樣。因此,它們可以在任何地方使用:您可以使用它們,將它們添加到表單或報表的數據環境中,將它們綁定到網格,在掃描循環中處理它們,等等。使用其他一些技術,特別是ADO和XML,必須將結果集轉換爲VFP遊標,然後才能在VFP中的許多地方使用它。
  • 與使用任何其他技術相比,將現有應用程序轉換爲使用遠程視圖更容易,特別是在已經使用本地視圖的情況下。
  • 由於可以將遠程視圖添加到窗體或報表的數據環境中,因此可以利用DE提供的可視化支持:拖放字段或整個光標來自動創建控件,通過從“屬性”窗口的組合框中選擇控件來輕鬆地將控件綁定到字段,等等。另外,根據AutoOpenTables和OpenViews屬性的設置,VFP將自動爲您打開遠程視圖。
  • 使用更改更新後端很容易:假設視圖的屬性設置正確,只需調用TABLEUPDATE()。事務處理和更新衝突檢測是內置的。
  • 遠程視圖在開發環境中很容易使用:只需使用然後瀏覽。

缺點

遠程視圖的缺點是:

  • 遠程視圖位於DBC中,因此必須在客戶端系統上維護和安裝另一組文件。
  • 由於遠程視圖的SQL SELECT語句是預定義的,因此不能動態更改它。雖然這對於典型的數據輸入表單來說是很好的,但是對於查詢和報表來說可能是一個問題。您可能需要從同一組數據創建多個視圖,每個視圖在所選字段、WHERE子句的結構等方面都有所不同。
  • 不能從遠程視圖調用存儲過程,因此遠程視圖需要直接訪問底層表。這可能是應用程序的數據庫管理員遇到的問題;有些DBA認爲,出於安全和其他原因,只能通過存儲過程進行數據訪問。另外,由於它們是在後端預編譯的,存儲過程的執行速度通常比SQL SELECT語句快得多。
  • 當使用TABLEUPDATE()將視圖中的更改寫入後端數據庫時,您幾乎沒有能力(通過設置一些屬性除外)控制VFP如何進行更新。
  • 與本地視圖一樣,如果使用SELECT*視圖從特定表檢索所有字段,並且該表在後端的結構發生更改,則該視圖無效,必須重新創建。他們只使用ODBC,所以他們被困在直接數據連接的“客戶機-服務器”模型中。他們不能利用ADO或XML的優點。
  • 如前所述,DBC鎖爭用是一個需要處理的問題。
  • 與其他類型的VFP遊標一樣,您不能將結果集傳遞到當前數據會話之外,更不用說傳遞到另一個應用程序或層。僅此限制就使得它們在n層應用程序中幾乎毫無用處。
  • 在VFP 8允許您指定使用USE語句打開遠程視圖時要使用的連接句柄之前,您幾乎沒有能力管理應用程序使用的連接。
  • 用於遠程視圖的連接信息在DBC中以純文本硬編碼(注意前面顯示的代碼中的用戶名和密碼)。這意味着黑客可以很容易地發現你後端王國的密鑰(比如用戶名和密碼),只需使用記事本打開DBC就可以了。從VFP 7開始,這並不是什麼大問題,因爲它允許您在使用USE命令打開遠程視圖時指定連接字符串,這意味着您可以在打開視圖之前動態組合服務器、用戶名和密碼(可能來自加密信息)。

基本上,這歸結爲一個控制問題:遠程視圖使處理後端數據變得容易,但代價是限制了對它們的控制。

何時使用

遠程視圖實際上只適用於客戶機-服務器、直接連接到數據的兩層應用程序。我相信,從長遠來看,如果使用n層設計開發,大多數應用程序將更加靈活、健壯,並且具有更長的保質期,因此,遠程視圖最適合於這樣的情況:您正在對現有應用程序進行升遷,而不想重新設計/重新開發它,或者您的開發團隊對其他技術沒有太多經驗。

SQL Passthrough(SPT)

VFP提供了許多函數,有時稱爲SQL passthrough(或SPT)函數,這些函數允許您訪問後端數據庫。SQLCONNECT()和SQLSTRINGCONNECT()連接到後端數據庫引擎;這兩個函數的區別在於,SQLCONNECT()需要現有的ODBC數據源(DSN)在用戶系統上定義,而SQLSTRINGCONNECT()只是將必要的信息直接傳遞給ODBC,即無DSNless連接。如您所料,SQLDISCONNECT()從後端斷開連接。SQLEXEC()向數據庫引擎發送一個命令(如SQL SELECT語句),通常(但不一定,取決於命令)將返回的結果放入VFP遊標中。
下面是一個打開到Northwind數據庫的連接的示例(假設有一個名爲“Northwind”的DSN定義瞭如何連接到此數據庫),檢索單個客戶記錄並將其顯示在瀏覽窗口中,然後關閉連接。

lnHandle = sqlconnect('Northwind') 
if lnHandle > 0 
  sqlexec(lnHandle, "select * from customers where customerid = 'ALFKI'") 
  browse 
  sqldisconnect(lnHandle) 
else 
  aerror(laErrors) 
  messagebox('Could not connect: ' + laErrors[2]) 
endif lnHandle > 0

要改用無DSN連接,請將SQLCONNECT()語句替換爲以下內容(將服務器名稱、用戶ID和密碼替換爲適當的值):

lnHandle = sqlstringconnect('Driver=SQL Server;Server=(local);' + ; 
  Database=Northwind;uid=sa;pwd=whatever')

DispLogin SQL設置控制ODBC是否顯示登錄對話框。儘管您可能認爲讓ODBC擔心用戶名和密碼很方便,但您無法控制對話框的外觀,也無法控制如果用戶輸入不正確的值會發生什麼。相反,您最好在自己的VFP對話框中向用戶請求適當的信息,然後將該信息傳遞給ODBC。如SQLCONNECT()函數的VFP幫助中所述,應該使用SQLSETPROP()將DispLogin設置爲3。
與其讓每個需要訪問遠程數據的組件管理自己的ODBC連接,不如使用一個對象,該對象的唯一職責是管理連接和訪問數據。基於Custom的SFConnectionMgr就是一個例子。它有幾個自定義屬性,其中一些屬性必須先設置,然後才能使用它連接到遠程數據源。

 

屬性 描述  
cDatabase 要連接到的數據庫;如果填寫了cDSN,則可以留空。
cDriver 要使用的ODBC驅動程序或OLE DB提供程序;如果填寫了cDSN,則可以留空。
cDSN 要連接到的ODBC DSN;留空則使用無DSN連接。
cErrorMessage 發生任何錯誤的消息。
cPassword 數據源的密碼;如果使用受信任的連接,則可以留空。
cServer 數據庫所在的服務器;如果填寫了cDSN,則可以留空。
cUserName 數據源的用戶名;如果使用受信任的連接,則可以留空。
lConnected 如果已連接到數據源則爲.T. 。

 

 

 

 

 

 

 

 

它有幾個自定義方法:

方法 描述
Connect 連接到數據源。
Disconnect 斷開數據源連接(Destroy調用,也可手動調用)。
Execute 對數據源執行語句。
GetConnection 返回連接句柄或ADO連接對象。
GetConnectionString 從各種連接屬性(受保護)返回連接字符串。
HandleError 當發生錯誤時設置cErrorMessage屬性(受保護)。

 

 

 

 

 

 

 

SFConnectionMgr不打算直接使用,但它是SFConnectionMgr ODBC和SFConnectionMgr ADO的子類,分別針對ODBC和ADO。這些子類重寫大多數方法以提供所需的特定行爲。讓我們看看SFConnectionMgrODBC。

Init方法將當前的dislogin設置保存爲自定義的ndislogin屬性,並將其設置爲3,這樣就永遠不會顯示登錄對話框。

This.nDispLogin = sqlgetprop(0, 'DispLogin') 
sqlsetprop(0, 'DispLogin', 3) 
dodefault()

Connect方法檢查我們是否已經連接,然後如果在cDSN屬性中指定了DSN,則使用SQLCONNECT()或生成連接字符串並使用SQLSTRINGCONNECT()函數。不管怎樣,如果連接成功,nHandle屬性都包含連接句柄,而lConnected是.T。如果連接失敗,nHandle是0,lConnected是.F,而cErrorMessage包含有關出錯的信息(在HandleError方法中設置,我們不會查看)。

* 如果尚未連接,請使用無DSN連接連接到指定的DSN或數據源。
local lcConnString 
with This 
  if not .lConnected 
    if empty(.cDSN) 
      lcConnString = .GetConnectionString() 
      .nHandle = sqlstringconnect(lcConnString) 
    else 
      .nHandle = sqlconnect(.cDSN, .cUserName, .cPassword) 
    endif empty(.cDSN) 
* 如果成功連接,則設置lConnected標誌,否則獲取錯誤信息。 
    .lConnected = .nHandle > 0 
    if not .lConnected 
      .nHandle = 0 
      .HandleError() 
    endif not .lConnected 
  endif not .lConnected 
endwith 
return This.lConnected

GetConnectionString從連接屬性的值返回連接字符串。

local lcSpecifier, ; 
  lcConnString 
with This 
  lcSpecifier  = iif(upper(.cDriver) = 'SQL SERVER', 'database=', ; 
    'dbq=') 
  lcConnString = 'driver=' + .cDriver + ';' + ; 
    iif(empty(.cServer),   '', 'server='   + .cServer   + ';') + ; 
    iif(empty(.cUserName), '', 'uid='      + .cUserName + ';') + ; 
    iif(empty(.cPassword), '', 'pwd='      + .cPassword + ';') + ; 
    iif(empty(.cDatabase), '', lcSpecifier + .cDatabase + ';') + ; 
    'trusted_connection=' + iif(empty(.cUserName), 'yes', 'no') 
endwith 
return lcConnString

Execute方法對數據源執行一個語句(如SQL SELECT命令)。它首先調用Connect以確保我們有一個連接,然後使用SQLEXEC()函數將語句傳遞給數據源。

lparameters tcStatement, ; 
  tcCursor 
local lcCursor, ; 
  llReturn 
with This 
  .Connect() 
  if .lConnected 
    lcCursor = iif(vartype(tcCursor) = 'C' and not empty(tcCursor), ; 
      tcCursor, sys(2015)) 
    llReturn = sqlexec(.nHandle, tcStatement, lcCursor) >= 0 
    if not llReturn 
      .HandleError() 
    endif not llReturn 
  endif .lConnected 
endwith 
return llReturn

Disconnect方法斷開與數據源的連接,並將nHandle設置爲0,將lConnected設置爲.F。

with This 
  if .lConnected 
    sqldisconnect(.nHandle) 
    .nHandle    = 0 
    .lConnected = .F. 
  endif .lConnected 
endwith

Destroy只需斷開連接並恢復保存的discogin設置。

This.Disconnect() 
sqlsetprop(0, 'DispLogin', This.nDispLogin) 
dodefault()

下面是一個示例(取自TestConnMgr.prg),它顯示瞭如何使用SFConnectionMgrODBC。它首先連接到SQL Server Northwind數據庫並獲取所有客戶記錄,然後連接到Access Northwind數據庫並再次檢索所有客戶記錄。當然,本例使用硬編碼連接信息;實際應用程序可能會將此信息存儲在本地表、INI文件或Windows註冊表中,以使其更加靈活。

loConnMgr = newobject('SFConnectionMgrODBC', 'SFRemote') 
with loConnMgr 
* 連接到SQL Server Northwind數據庫並獲取客戶記錄。
  .cDriver   = 'SQL Server' 
  .cServer   = '(local)' 
  .cDatabase = 'Northwind' 
  .cUserName = 'sa' 
  .cPassword = '' 
  do case 
    case not .Connect() 
      messagebox(.cErrorMessage) 
    case .Execute('select * from customers') 
      browse 
      use 
    otherwise 
      messagebox(.cErrorMessage) 
  endcase 
* 現在連接到Access Northwind數據庫並獲取客戶記錄。
  .Disconnect() 
  .cDriver   = 'Microsoft Access Driver (*.mdb)' 
  .cServer   = '' 
  .cDatabase = 'd:\Program Files\Microsoft Visual Studio\VB98\Nwind.mdb' 
  .cUserName = '' 
  .cPassword = '' 
  do case 
    case not .Connect() 
      messagebox(.cErrorMessage) 
    case .Execute('select * from customers') 
      browse 
      use 
    otherwise 
      messagebox(.cErrorMessage) 
  endcase 
endwith

優勢

使用SPT的優點是:

  • 與遠程視圖相比,您在數據訪問方面具有更大的靈活性,例如使用SQLEXEC()函數調用存儲過程。
  • 您可以根據需要隨時更改連接信息。例如,可以將用戶名和密碼存儲爲加密值,並且僅在將其用於SQLCONNECT()或SQLSTRINGCONNECT()函數之前對其進行解密。您還可以更改服務器甚至後端數據庫引擎(例如,只需更改SQLSTRINGCONNECT()調用中指定的ODBC驅動程序,就可以在SQL Server和Access數據庫之間切換)。如前所述,與以前的遠程視圖相比,這幾乎沒有優勢,現在VFP 7允許您在USE命令上指定連接字符串。
  • 您可以根據需要更改SQL SELECT語句。例如,您可以輕鬆地更改字段列表、WHERE子句(例如更改涉及的字段或將其全部刪除)、表等。
  • 您不需要DBC來使用SPT,因此不需要維護或安裝任何東西,鎖爭用不是問題,而且您不必擔心當後端表的結構更改時,SELECT*語句將變爲無效。
  • 與遠程視圖一樣,SPT調用的結果集是VFP遊標,它可以在VFP中的任何地方使用,而不存在ADO和XML所具有的轉換問題。
  • 儘管您必須自己編寫代碼(這將在“缺點”下進行更詳細的討論),但您可以更好地控制如何進行更新。例如,可以使用SQL SELECT語句來創建遊標,但可以調用存儲過程來更新後端表。
  • 你可以管理自己的關係。例如,您可能希望使用與前面討論的類似的連接管理器來管理應用程序在一個位置使用的所有連接。

缺點

使用SPT的缺點是:

  • 這需要更多的工作,因爲您必須對所有內容進行編碼:創建和關閉連接、要執行的SQL SELECT語句等等。您沒有像視圖設計器這樣好的可視化工具來顯示哪些字段存在於後端的哪些表中。
  • 無法將SPT創建的遊標可視化地添加到窗體或報表的數據環境中。相反,您必須對光標的打開進行編碼(例如,在BeforeOpenTables方法中),您必須手動創建控件,並且您必須通過自己鍵入來填充綁定屬性(例如,ControlSource)(輸入別名和字段名時不要鍵入錯誤,否則表單將無法工作)。
  • 與開發環境中的遠程視圖相比,它們更難使用:您必須創建一個連接,然後使用SQLEXEC()調用來獲取要查看的數據,而不是僅僅發出USE命令。如果您創建一組PRG來爲自己完成工作(例如UseCustomers.prg,它打開並顯示Customers表的內容),則可以使您的工作更輕鬆。您還可以使用SQL Server企業管理器(或類似的工具)檢查表的結構和內容。您甚至可以創建一個DBC和一組只在開發環境中使用的遠程視圖,作爲查看數據的快速方法。
  • 與遠程視圖和其他類型的VFP遊標一樣,不能在當前數據會話之外傳遞結果集。相反,您可能必須傳遞用於創建遊標的信息(SQL SELECT語句或存儲過程調用,甚至可能傳遞連接信息),並讓另一個對象自己完成工作。
  • 使用SPT創建的遊標可以更新,但您必須使用對SendUpdates、Tables、KeyFieldList、UpdateableFieldList和UpdateNameList屬性的一系列CURSORSETPROP()調用來實現它們。另外,你必須自我管理事務處理和更新衝突檢測。
  • 由於SPT遊標不像遠程視圖那樣定義,因此您不能像使用遠程視圖那樣使用SPT輕鬆地在本地和遠程數據之間切換(只需更改在窗體或報表中打開的視圖)。
  • 與遠程視圖一樣,SPT僅基於ODBC,因此不能利用ADO或XML的優點。

何時使用

與遠程視圖一樣,SPT最適合於與數據直接連接的客戶機-服務器兩層應用程序。由於將現有應用程序轉換爲SPT要比遠程視圖或遊標適配器(稍後我們將看到)做得更多,SPT最適合於實用程序、簡單應用程序或窄焦點情況。

ADO

OLE DB和ADO是微軟通用數據訪問策略的一部分,在這種策略中,任何類型的數據都可以以任何格式存儲在任何地方,而不僅僅是存儲在本地服務器上的關係數據庫中,可以供任何需要它的應用程序使用。OLE DB提供程序類似於ODBC驅動程序:它們提供了一種標準的、一致的訪問數據源的方法。各種OLE DB提供程序可用於特定的DBMS(SQL Server、Oracle、Access/Jet等),而Microsoft爲ODBC數據源提供OLE DB提供程序。

  • 連接:這是負責與數據源通信的對象。
  • 記錄集:這相當於VFP光標:它具有定義的結構,包含數據集中的數據,並提供屬性和方法來添加、刪除或更新記錄、從一個記錄移動到另一個記錄、篩選或排序數據以及更新數據源。
  • 命令:與簡單的SELECT語句相比,該對象提供了執行更高級查詢的方法,例如參數化查詢和調用存儲過程。

下面是一個示例(ADOExample.prg),它從Northwind數據庫獲取所有巴西客戶,並顯示客戶ID和公司名稱。注意,連接對象處理連接(記錄集的ActiveConnection屬性設置爲連接對象),而記錄集處理數據。

local loConn as ADODB.Connection, ; 
  loRS as ADODB.Recordset 
loConn = createobject('ADODB.Connection') 
loConn.ConnectionString = 'provider=SQLOLEDB.1;data source=(local);' + ; 
  'initial catalog=Northwind;uid=sa;pwd=' 
loConn.Open() 
loRS = createobject('ADODB.Recordset') 
loRS.ActiveConnection = loConn 
loRS.LockType         = 3  && adLockOptimistic 
loRS.CursorLocation   = 3  && adUseClient 
loRS.CursorType       = 3  && adOpenStatic 
loRS.Open("select * from customers where country='Brazil'") 
lcCustomers = '' 
do while not loRS.EOF 
  lcCustomers = lcCustomers + loRS.Fields('customerid').Value + chr(9) + ; 
    loRS.Fields('companyname').Value + chr(13) 
  loRS.MoveNext() 
enddo while not loRS.EOF 
messagebox(lcCustomers) 
loRS.Close() 
loConn.Close()

由於其面向對象的特性和功能,ADO一直是n層開發的首選數據訪問機制(儘管隨着XML變得越來越流行,這種情況正在迅速改變)。與ODBC不同,您不必直接連接到數據源。
有關ADO和在VFP中使用ADO的詳細信息,請參閱John Petersen的“ADO Jumpstart For Visual FoxPro Developers”白皮書,該白皮書可從VFP主頁(http://msdn.microsoft.com/vfoxpro;按照“技術資源”、“技術文章”和“com和ActiveX開發”鏈接訪問文檔)。

優勢

使用ADO的優點是:

  • 與SPT一樣,與調用存儲過程等遠程視圖相比,在數據訪問方面具有更大的靈活性。
  • 您可以根據需要動態更改連接信息,例如加密用戶名和密碼、更改服務器,甚至後端數據庫引擎。
  • 您可以根據需要更改SQL SELECT語句。
  • 不涉及DBC。
  • 儘管在簡單的場景中性能差異並不顯著(事實上,在我的測試中,ODBC比ADO快),但ADO在Web服務器等大量使用的應用程序中更具可伸縮性。
  • 與VFP遊標不同,ADO對象可以在當前數據會話之外傳遞給應用程序中的另一個組件、另一個應用程序或COM對象(例如Excel或中間層組件)或另一臺計算機。您甚至可以使用遠程數據服務(RDS)通過HTTP發送它們;有關更多信息,請參閱John Petersen的白皮書,儘管當涉及防火牆時這是一個問題。
  • ADO是面向對象的,因此您可以像處理對象一樣處理數據。
  • 根據ADO記錄集的設置方式,ADO記錄集可以自動更新,而無需任何額外工作(調用Update或UpdateBatch方法除外)。事務處理和更新衝突檢測是內置的。
  • 你可以管理自己的關係(?連接)。
  • 您可以輕鬆地將記錄集持久化爲本地文件,然後重新加載並繼續工作,最後更新後端數據源。這使得它對於“road warrior”應用程序的選擇要比遠程視圖或SPT好得多。

缺點

ADO的缺點是:

  • 這需要更多的工作,因爲您必須對所有內容進行編碼:創建和關閉連接、要執行的SQL SELECT語句等等。您沒有像視圖設計器這樣好的可視化工具來顯示哪些字段存在於後端的哪些表中。
  • ADO記錄集不是VFP遊標,因此不能在需要遊標的地方使用,例如網格和報表。VFPCOM實用程序(可從VFP主頁http://msdn.microsoft.com/vfoxpro下載)中有一些函數可以將記錄集轉換爲遊標,反之亦然,但使用這些函數可能會影響性能,特別是對於大型數據集,而且它們已知某些數據類型存在問題。
  • 沒有對ADO記錄集的可視化支持,因此必須對其創建和打開進行編碼,必須手動創建控件,並且必須通過自己鍵入來填寫綁定屬性(如ControlSource)。這比SPT的工作還要多,因爲語法不僅僅是CURSOR.FIELD,而是RecordSet.Fields('FieldName').Value。
  • 它們是開發環境中最難使用的技術,因爲您必須對所有內容進行編碼:建立連接、檢索數據、在記錄之間來回移動。您甚至無法通過瀏覽查看結果集的外觀(除非使用VFPCOM或其他方法將記錄集轉換爲遊標)。
  • 與使用ODBC創建的遊標相比,ADO的學習曲線更大。
  • 您可能需要確保客戶端安裝了最新版本的Microsoft數據訪問組件(MDAC),以確保其OLEDB和ADO版本與您的應用程序所需的相匹配。
  • ADO是一種僅限Windows的技術。

何時使用

在其他組件之間來回傳遞數據時,ADO很容易使用。例如,VFP COM對象的方法可以很容易地將ADO記錄集返回到Excel VBA代碼,然後該代碼可以處理和顯示結果。
如果您正在使用n層體系結構設計應用程序,那麼ADO可能是一個不錯的選擇,如果您已經熟悉它或者已經爲它準備好了基礎設施。然而,XML正迅速成爲n層應用程序的首選機制,因此我希望ADO在這方面的應用越來越少。

XML

XML(可擴展標記語言)並不是一種真正的數據訪問機制;它實際上是一種傳輸技術。數據被打包成具有結構化格式的文本,然後運到某個地方。然而,由於XML只是文本,所以它比其他技術有很多優勢(我們稍後將討論)。
幾年前,微軟“發現”了XML,並從那時起在幾乎所有地方都實現了它。內置在.NET框架ADO.NET中的數據訪問技術以XML爲基礎(事實上,一個簡單的觀點是ADO.NET實際上只是一組包裝類,通過OOP接口公開XML數據)。基於SOAP(簡單對象訪問協議)的Web服務使用XML作爲通信和數據傳輸的基礎。XML甚至正迅速成爲n層應用程序的首選數據傳輸機制,長期以來,n層應用程序更青睞ADO。
VFP 7添加了幾個與XML一起工作的函數:XMLTOCURSOR(),它將XML轉換爲遊標;CURSORTOXML(),它將執行相反的操作;XMLUPDATEGRAM(),它將updategram(以特定格式表示對數據的更改的XML)從對遊標的更改中生成。下面是一些VFP代碼(取自XMLExample1.prg),展示了VFP如何處理XML:

* 獲取ALFKI客戶的信息並顯示原始XML。
close databases all 
lcXML = GetCustomerByID('ALFKI') 
strtofile(lcXML, 'ALFKI.XML') 
modify file ALFKI.XML 
erase ALFKI.XML 
*將其放入遊標中,瀏覽並進行更改,然後查看updategram。
xmltocursor(lcXML, 'CUSTOMERS') 
set multilocks on 
cursorsetprop('Buffering', 5) 
browse 
lcUpdate = xmlupdategram() 
strtofile(lcUpdate, 'UPDATE.XML') 
modify file UPDATE.XML 
* 通過設置KeyFieldList屬性,updategram將只包含key和changed字段。 
cursorsetprop('KeyFieldList', 'cust_id') 
lcUpdate = xmlupdategram() 
strtofile(lcUpdate, 'UPDATE.XML') 
modify file UPDATE.XML 
erase UPDATE.XML 
close databases all 
* 此函數將指定的客戶記錄返回爲XML。
function GetCustomerByID(tcCustomerID) 
local lcXML 
open database (_samples + 'data\testdata') 
select * from customer where cust_id = tcCustomerID into cursor Temp 
cursortoxml('Temp', 'lcXML', 1, 8, 0, '1') 
use in Temp 
use in Customer 
return lcXML

注意,在版本8之前,雖然VFP可以創建一個XML updategram,但它沒有一個簡單的方法來使用它(即,更新VFP表)。Visual FoxPro MVP Alex Feldstein爲此編寫了一個例程(http://fox.wikis.com/wc.dll?Wiki~XMLUpdateGramParse),但在VFP 8中,可以使用新的XMLAdapter類的實例來完成此操作(我們將在“VFP:Advanced中的數據策略”文檔中查看該類)。
下面是一個示例(XMLExample2.prg),它使用SQLXML通過Web服務器從SQL Server獲取客戶記錄(我們將在高級篇中更詳細地討論SQLXML),然後將更改發送回。

* 獲取ALFKI客戶的信息並顯示原始XML。
close databases all 
lcXML = GetNWCustomerByID('ALFKI') 
strtofile(lcXML, 'ALFKI.XML') 
modify file ALFKI.XML 
erase ALFKI.XML 
* 將其放入遊標中,瀏覽並進行更改。
xmltocursor(lcXML, 'CUSTOMERS') 
cursorsetprop('KeyFieldList', 'customerid') 
set multilocks on 
cursorsetprop('Buffering', 5) 
browse 
* 獲取updategram並將更改保存到SQL Server。
lcUpdate = xmlupdategram() 
SaveNWCustomers(lcUpdate) 
use 
* 此函數使用SQLXML將指定的客戶記錄獲取爲XML。
function GetNWCustomerByID(tcCustomerID) 
local loXML as MSXML2.XMLHTTP 
loXML = createobject('MSXML2.XMLHTTP') 
loXML.open('POST', 'http://localhost/northwind/template/ ' + ; 
  'customersbyid.xml?customerid=' + tcCustomerID, .F.) 
loXML.setRequestHeader('Content-type', 'text/xml') 
loXML.send() 
return loXML.responseText 
* 此函數使用SQLXML將指定的客戶記錄獲取爲XML。
function SaveNWCustomers(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)

優勢

使用XML有很多好處:

  • 與遠程視圖相比,您具有相同的優勢,例如沒有DBC和易於更改的SQL SELECT語句,就像SPT和ADO一樣。
  • 由於XML並不是一種真正的數據訪問機制,因此在使用XML進行數據訪問時具有最大的靈活性。例如,可以使用VFP CURSORTOXML()函數、Web服務、中間層組件、ADO.NET數據集、XMLHTTP和許多其他機制將數據從一個位置獲取到另一個位置。
  • 因爲XML只是文本,所以可以在任何地方傳遞,甚至可以通過防火牆(這是ADO的一個問題)。
  • XML DOM對象爲XML數據提供了一個面向對象的接口。
  • XML很容易持久化到任何地方:文件、表中的備註字段等。
  • XML完全獨立於平臺/操作系統。
  • XML是大衆可讀的,與其他機制的二進制格式不同。

缺點

使用XML還有一些缺點:

  • 儘管XML的格式很簡單,但與其他機制相比,它的學習曲線更大。XML DOM對象有自己的對象模型,有各種新技術(和縮寫!)要學,像模式、XSLT、XPath、XDR和XQuery。
  • 同一組數據在XML中可能比在其他機制中大很多,因爲每個元素都有開始和結束標記。例如,VFP的CUSTOMER示例表的DBF文件26257字節長而XML爲40586字節長。
  • 雖然CURSORTOXML()速度很快,但使用XML DOM對象進行工作的XMLTOCURSOR()速度可能會很慢,尤其是在數據量很大的情況下。
  • XML標準仍在不斷髮展。

何時使用

XML有很多優點,包括存儲配置設置、在應用程序組件之間傳遞少量數據、在備註字段中存儲結構化數據等等,XML非常適合於n層應用程序,因爲它易於傳輸(在組件之間或穿牆)和轉換到數據集(如VFP遊標)或從中轉換。使用XML Updategrams(和更新的Diffgrams),可以限制傳輸的數據量。如果您正在啓動新的n層項目,這顯然是要使用的數據訪問機制。

CursorAdapter

你可能注意到的一點是,我們所研究的每一種機制都與其他機制完全不同。這意味着每種機制都有一個新的學習曲線,將現有應用程序從一種機制轉換爲另一種機制是一項非常重要的任務。
在我看來,CursorAdapter是VFP 8中最大的新特性之一。我覺得他們這麼酷的原因是:

  • 它們使使用ODBC、ADO或XML變得容易,即使您不太熟悉這些技術。
  • 它們爲遠程數據提供了一致的接口,而不管您選擇何種機制。
  • 它們使從一種機制切換到另一種機制變得容易。

這是最後一點的例子。假設您有一個應用程序使用帶有CursorAdapter的ODBC來訪問SQL Server數據,出於某種原因,您希望改爲使用ADO。您只需更改CursorAdapters的DataSourceType並更改到後端數據庫的連接,就完成了。應用程序中的其他組件既不知道也不關心這一點;它們仍然看到同一個遊標,而不管用於訪問數據的機制如何。
我們將仔細查看高級文檔中的CursorAdapter。不過,在此期間,這裏有一個示例(CursorAdapterExample.prg),它從Northwind數據庫的Customers表中爲巴西客戶獲取某些字段。遊標是可更新的,因此如果您在遊標中進行了更改,請將其關閉,然後再次運行程序,您將看到您的更改已保存到後端。

local loCursor as CursorAdapter, ; 
  laErrors[1] 
loCursor = createobject('CursorAdapter') 
with loCursor 
  .Alias              = 'Customers' 
  .DataSourceType     = 'ODBC' 
  .DataSource         = sqlstringconnect('driver=SQL Server;' + ; 
    'server=(local);database=Northwind;uid=sa;pwd=;trusted_connection=no') 
  .SelectCmd          = "select CUSTOMERID, COMPANYNAME, CONTACTNAME " + ; 
    "from CUSTOMERS where COUNTRY = 'Brazil'" 
  .KeyFieldList       = 'CUSTOMERID' 
  .Tables             = 'CUSTOMERS' 
  .UpdatableFieldList = 'CUSTOMERID, COMPANYNAME, CONTACTNAME' 
  .UpdateNameList     = 'CUSTOMERID CUSTOMERS.CUSTOMERID, ' + ; 
    'COMPANYNAME CUSTOMERS.COMPANYNAME, CONTACTNAME CUSTOMERS.CONTACTNAME' 
  if .CursorFill() 
    browse 
  else 
    aerror(laErrors) 
    messagebox(laErrors[2]) 
  endif .CursorFill() 
endwith

優勢

CursorAdapter的優點本質上是所有其他技術的結合。

  • 根據它的設置方式(例如,如果它是完全獨立的),從CursorAdapter子類打開遊標幾乎和打開遠程視圖一樣簡單:只需實例化子類並調用CursorFill方法(甚至可以從Init調用它,使其成爲一個單步操作)。
  • 將現有應用程序轉換爲使用CursorAdapter比使用SPT創建的遊標更容易。
  • 與遠程視圖一樣,您可以將CursorAdapter添加到窗體或報表的數據環境中,並利用DE提供的可視化支持:拖放字段以自動創建控件,通過從“屬性”窗口的組合框中選擇控件來輕鬆將控件綁定到字段,等等。
  • 更改後端的變化很容易:假設視圖的屬性設置正確,只需調用TABLEUPDATE()。
  • 因爲CursorAdapter創建的結果集是一個VFP遊標,所以它們可以在VFP中的任何位置使用:在網格中、報告中、在掃描循環中處理,等等。即使數據源來自ADO和XML,這也是正確的,因爲CursorAdapter會自動爲您處理與遊標之間的轉換。
  • 在數據訪問方面有很大的靈活性,例如調用存儲過程或中間層對象。
  • 您可以根據需要動態更改連接信息。
  • 您可以根據需要更改SQL SELECT語句。
  • 你不需要DBC。
  • 它們可以與ODBC、ADO、XML或本機表一起工作,允許您根據需要利用這些技術中的任何一種,甚至是交換技術的優點。
  • 儘管您必須自己編寫代碼(這將在“缺點”一節中進行更詳細的討論),但是您可以更好地控制如何進行更新。例如,可以使用SQL SELECT語句來創建遊標,但可以調用存儲過程來更新後端表。
  • 你可以管理自己的連接(?關係)。

缺點

CursorAdapter沒有太多缺點:

  • 雖然CursorAdapter生成器是一個很好的輔助工具,但是您不能使用像視圖設計器這樣好的可視化工具來創建CursorAdapter。
  • 與其他類型的VFP遊標一樣,不能在當前數據會話之外傳遞結果集。但是,由於CursorAdapter實際上是用於UI層的,所以這不是什麼大問題。
  • 在報告中使用它們很難;我們將在高級篇中更詳細地討論這一點。
  • 像所有新技術一樣,必須掌握一條學習曲線。

何時使用

因爲CursorAdapter創建VFP遊標,所以您不太可能在n層應用程序的中間層使用它們。然而,在UI層中,我看到CursorAdapter替換了所有遠程數據訪問的其他技術,甚至在將來可能會被升級的新應用程序中替換了Cursor。

總結

本文討論了ODBC(無論是遠程視圖還是SQL Passthrough)、ADO和XML作爲訪問非VFP數據(如SQL Server或Oracle)的方法的優缺點。通常,您應該爲特定應用程序選擇哪種機制取決於許多因素。然而,在VFP 8中使用新的CursorAdapter技術可以更容易地過渡到遠程數據訪問,並且在需要時更容易在機制之間切換。
在“VFP的數據策略:高級篇”文檔中,我們將詳細討論CursorAdapter類,查看使用ODBC、ADO和XML訪問本機數據或非VFP數據的細節。我們還將研究如何創建可重用的數據類,並討論如何在報表中使用CursorAdapter。

作者介紹:

Doug Hennig是Stonefield Systems Group Inc.的合作伙伴。他是獲獎的Stonefield數據庫工具包(SDT)的作者和獲獎的Stonefield查詢的共同作者。他是《黑客視覺FoxPro 7.0指南》的合著者(與Tamar Granor、Ted Roche和Della Martin一起)和《視覺FoxPro 7.0的新特性》的合著者(與Tamar Granor和Kevin McNeish一起),均來自Hentzenwerke出版社,在Pinnacle Publishing的Pros Talk VisualFoxPro系列中,“VisualFoxPro數據字典”的作者。他在FoxTalk上寫了每月的“可重用工具”專欄。他是《黑客指南》和《基礎知識》的技術編輯,這兩本書都來自亨森沃克出版社。自1997年以來,道格在每次微軟FoxPro開發者大會(DevCon)以及北美各地的用戶團體和開發者大會上都發表過演講。他是微軟最有價值的專業人士(MVP)和認證專業人士(MCP)。

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