VFP的數據策略:基礎篇

VFP的數據策略:基礎篇html

做者:Doug Hennig  翻譯:老瓷前端

概述

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

介紹

愈來愈多的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框架

優點

遠程視圖的優勢是:less

  • 您可使用視圖設計器可視化地建立遠程視圖。好吧,在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)。

相關文章
相關標籤/搜索