电竞比分网-中国电竞赛事及体育赛事平台

分享

Visual FoxPro 9 C/S(ODBC)方面極富人性化的增強(qiáng)

 Alkaid2015 2015-06-11

Visual FoxPro 9 C/S(ODBC)方面極富人性化的增強(qiáng)

作者: mihu


    2004年6月,微軟公司如期發(fā)布了全球Foxer翹首期盼代號為Europa的Visual FoxPro 9beta 版(以下簡稱VFP9),下文是我學(xué)習(xí)、探索 VFP9 C/S方面新增功能時的一些心得,供大家一起分享。

    自從VFP 8 開始,在C/S方面提供了一個CursorAdapter 類, CursorAdapter 是一個基于松散耦合思想設(shè)計的對象化的 Cursor 處理模型。對 CursorAdapter 類很多人對其褒貶不一,特別是一些老的Foxer認(rèn)為做C/S系統(tǒng)用SPT就足夠了,何必再增加一個類呢?但我可以這么說,從VFP9 開始,幾個CursorAdapter新增加的功能,相信足以使那些純使用 SPT 的Foxer 心動不已了。

    下面我先談?wù)刅FP9 在 CursorAdapter 部分的幾個增加和增強(qiáng)。

1. 屬性值可以超過255 個字符

    大家都知道,VFP的表單、類庫和報表等其實都是以DBF形式存放,VFP9以前屬性是以(C)字符型存放,VFP9中做了修改,以(M)備注型存放,這樣使得在VFP9開始,屬性值突破了255個字符的限制。
    用過CursorAdapter 類的人都知道,VFP8 時其幾個屬性 SelectCmd、CursorSchema、UpdateNameList、UpdatableFieldList的長度不能超過255個字符,這在VFP8時讓人感覺是一件非?;屯纯嗟氖虑椋?dāng)后臺表的字段數(shù)一多,連使用它自身生成器生成的字符串都要報錯,不能保存,這點造成CursorAdapter使用起來極為不便,使得CursorAdpter的生成器變成了擺設(shè),也是全球Foxer要求解決的呼聲最多的地方,現(xiàn)在這個問題在VFP9 中終于得到了徹底解決。

2. NoData 和 UseCursorSchema 屬性

    VFP8 時當(dāng)把CursorAdapter 設(shè)計時放到表單的數(shù)據(jù)環(huán)境里,使用CursorFill()里的這兩個參數(shù)極為不便,尤其是對初學(xué)者,現(xiàn)在好了,把這兩個屬性單獨列了出來。


3. TimeStamp 時間戳字段

    VFP8 的CursorAdapter 雖然 WhereType 可以等于 4 ,可是其實時間戳字段真正在更新時卻過濾掉了,因為你前臺是不更改這個字段的,所以CursorAadpter,更新后臺時是不會自動把這個時間戳字段加進(jìn)更新語句,所以根本不起任何作用?,F(xiàn)在VFP9 的CursorAdapter 增加了 TimestampFieldList屬性,如果你的后臺表里有時間戳的話,只要設(shè)置一下這個屬性,然后設(shè)置WhereType = 4即可,注意,UpdateNameList和UpdatableFieldList里還是要把時間戳字段加進(jìn)列表,呵呵個人感覺,微軟工程師在偷懶,其實這個只要判斷一下就可以了,何必還要加進(jìn)UpdateNameList和UpdatableFieldList,期待VFP9 SP1能加以改進(jìn)。

4. RecordRefresh()


    VFP8時CursorAdapter只能用 CursorRefresh() 來刷新前臺,可很多時候,我們可能只需要刷新其中一條或者幾條記錄的數(shù)據(jù),可CursorRefresh(),如果前臺記錄有 1000 條,也要全部重新讀一遍,讀取完畢以后,記錄指針卻始終定位在第一條記錄,這樣既巨大的浪費了網(wǎng)絡(luò)資源,同時很多情況下還要花費很大的精力重新定位記錄指針。


    現(xiàn)在 VFP9 增加了這個極富人性化的方法-----RecordRefresh(),能做到任意刷新此Cursor里的任意一條或者連續(xù)幾條記錄,而且當(dāng)前記錄指針保持不變,看到這里相信做過C/S程序的朋友們是不是有一種躍躍欲試的感覺?這個可以期盼已久的功能啊。


RecordRefresh()里有2個參數(shù),RecordRefresh(nRecords,nRecordOffset)
1) nRecords 表示要刷新幾條記錄
2) nRecordOffset記錄偏移量,指是當(dāng)前記錄開始加幾條記錄


例子1,比如當(dāng)前記錄號是第5條,我要刷新第7、8兩條記錄,
oCa.RecordRefresh(2,-2)


例子2, 只刷新當(dāng)前記錄,
oCa.RecordRefresh(1)


5. CursorAdapter 的 Auto-Refresh


    VFP9提供了記錄的Auto-Refresh,其作用、功能、效果和前面介紹的RecordRefresh() 基本相似,有異曲同功之妙用。


讓我們來看看具體是怎么使用的吧:


InsertCmdRefreshFieldList、InsertCmdRefreshKeyFieldList、InsertCmdRefreshCmd

這三個新增的屬性是針對新增記錄時使用,一般我們只需要設(shè)置InsertCmdRefreshFieldList、InsertCmdRefreshKeyFieldList即可,從字面已經(jīng)非常好理解需要設(shè)置的內(nèi)容,InsertCmdRefreshKeyFieldList如果和CursorAdapter的KeyFieldList相同,可以不設(shè)置,一般情況下都是相同的。


UpdateCmdRefreshFieldList、UpdateCmdRefreshKeyFieldList、UpdateCmdRefreshCmd

這三個新增的屬性是針對修改記錄時使用的,一般我們也只需設(shè)置UpdateCmdRefreshFieldList和UpdateCmdRefreshKeyFieldList即可。UpdateCmdRefreshKeyFieldList如果和CursorAdapter的KeyFieldList相同,也可以不設(shè)置。

    讓我們設(shè)置好這些屬性以后看看效果吧,當(dāng)TableUpdate()提交后臺成功以后,記錄就自動刷新了,而且當(dāng)前的記錄指針保持不變,這難道不正是我們想要的嗎?

    請注意,RecordRefresh()和Auto-Refresh 的區(qū)別,雖然RecordRefresh() 也能刷新多條指定的記錄,可只能是連續(xù)的幾條記錄,而Auto-Refresh卻只刷新已修改過的記錄。RecordRefresh()刷新的記錄可以自己來指定,可Auto-Refresh 刷新的記錄卻是 CursorAdapter 來控制的。


    使用RecordRefrsh()和Auto-Refresh功能時有點需要特別注意:大家讀到這里應(yīng)該可以看出,其實此三項功能,都是依靠關(guān)鍵字來定位記錄的,可當(dāng)我們用自增量型字段做關(guān)鍵字時,當(dāng)新增記錄時,其值是更新成功以后才能得到,所以當(dāng)在前臺使用RecordRefrsh()和Auto-Refresh時會因無法定位記錄而要出錯或得不到正確結(jié)果。所以當(dāng)使用自增量型字段做關(guān)鍵字時要特殊處理一下,就是要另外設(shè)置一下InsertCmdRefreshCmd,通過InsertCmdRefreshCmd來取得剛生成的自增量值,這樣就可以正常使用CusorAdapter的AutoRefrsh行級刷新功能了。

6. DelayedMemoFetch()


    VFP8 的CursorAdapter 里有一個屬性:FetchMemo,當(dāng)設(shè)置FetchMemo = .F. 時,本表里的所有Memo 字段都不讀取到前臺,這樣來大大提高了讀取數(shù)據(jù)時的效率,特別是當(dāng)Memo字段里內(nèi)容多的時候,可是當(dāng)我們需要Memo字段里的內(nèi)容時怎么辦呢?這點是我們一直以來不管用遠(yuǎn)程視圖、SPT還是用CursorAdapter做 C/S 程序時的痛苦矛盾之處,現(xiàn)在我們終于在VFP9的CursorAdapter 里找到了完美的解決辦法。


    先用文字來說明一下 VFP9 的CursorAdapter 的解決方法,當(dāng) CursorFill() 時,所有Memo字段的內(nèi)容全部為空,以提高讀取記錄時的效率,但當(dāng)光標(biāo)移動到這條記錄的這個字段時,自動從后臺讀取這條記錄的當(dāng)前Memo字段的內(nèi)容到前臺,填充進(jìn)CursorAdapter里,以我實際操作下來的感覺,幾乎根本感覺不到這個讀取過程,而數(shù)據(jù)確確實實讀到了前臺。


整個設(shè)置過程如下:
1)oCa.FetchMemo = .F.
2)oCa.FetchMemoDataSourceType = oCa.DataSourceType
3)oCa.FetchMemoDataSource = oCa.DataSource
4)oCa.FetchMemoCmdList  這個是最關(guān)鍵的,也是最麻煩的:
oCA.FetchMemoCmdList= "f1 <SELECT f1 FROM testCAMemoFetch WHERE f0=?EVALUATE(this.RefreshAlias +'.f0')>, f2 <SELECT f2 FROM testCAMemoFetch WHERE f0=?EVALUATE(this.RefreshAlias +'.f0')>"


f1,f2這里是Memo字段名簡稱
f0一般是關(guān)鍵字段名


this.RefreshAlias可以是this.Alias


我翻譯一下,就應(yīng)該比較好理解了,意思如下:” Memo字段名 <select Memo字段名 From 后臺表名 where 關(guān)鍵字段名=?前臺的Alias名.關(guān)鍵字段內(nèi)容>,…..”


只要設(shè)置好了以上屬性,當(dāng)前臺記錄移動到Memo字段時 CursorAdapter 會自動調(diào)用DelayedMemoFetch(),來讀取Memo字段內(nèi)容。


注意:1)DelayedMemoFetch如果按照以上設(shè)置,只是從現(xiàn)象上看實現(xiàn)了“DelayedMemoFetch”,實際上并未真正提高效率,Memo型內(nèi)容還是一次下載到了本地,這個估計也能算是一個BUG吧,目前的解決辦法是,例如后臺是SQL Server數(shù)據(jù)庫,
原oCa.SelectCmd為:

SELECT Field1,Field2,MemoField1,MemoField2,MemoField3 FROM TabelName

改成

SELECT Field1,Field2,cast('' as text) as MemoField1,cast('' as text) as MemoField2,cast('' as text) as MemoField3 FROM TableName

DelayedMemoFetch其他設(shè)置不變,這樣就真正實現(xiàn)了“DelayedMemoFetch”。

     2):如果是異步的話,使用DelayedMemoFetch時,要另外設(shè)置不同的oCa.FetchMemoDataSource

     3): "DelayedMemoFetch"功能不光針對Memo有效,對VFP9新增的字段類型blob同樣有效。

     RecordRefrsh()、Auto-Refresh和DelayedMemoFetch() 此三項功能是 SPT 無法做到的,RecordRefrsh()、Auto-Refresh做到了前臺Cursor的行級(記錄級)刷新,而DelayedMemoFetch()實現(xiàn)了字段級的刷新,合理使用好此三項功能,能極大的提高整個 C/S 系統(tǒng)的效率,降低網(wǎng)絡(luò)和服務(wù)器開銷。

7. blob
    VFP9新增加了幾種數(shù)據(jù)類型,其中一種就是blob--二進(jìn)制大型對象,以前我們在后臺保存圖片的時候,在保存讀取前后有一個FileToStr()和StrToFile()過程,這使得編程相當(dāng)麻煩,同時中間過程中還要產(chǎn)生臨時文件,現(xiàn)在我們完全可以利用VFP9新增加的這個數(shù)據(jù)類型,結(jié)合VFP9 Image控件新增加的PictureVal屬性,直接將blob類型綁定在Image控件的PictureVal屬性上,當(dāng)中無需做任何轉(zhuǎn)換。具體用CursorSchema做時只需在oCa.CursorSchema里設(shè)置相應(yīng)字段類型為(W)型,同時設(shè)置oCa.UseCursorSchema = .T.即可。
    據(jù)具體測試,如果后臺是SQL Server,blob型最佳對應(yīng)類型是Image型,當(dāng)然用Text也可以,但發(fā)現(xiàn),用Text型放置大圖片會發(fā)生丟失字節(jié)的現(xiàn)象,而放置其他文件卻沒什么問題。
放置圖片的blob字段在報表中的應(yīng)用:

此主題相關(guān)圖片如下:


此主題相關(guān)圖片如下:


示例程序如下: 
CLOSE ALL
CLEAR ALL
LOCAL cDeafultPath, cDataPath
PRIVATE oRL AS ReportListener
cDefaultPath = ADDBS(JUSTPATH(SYS(16)))
SET DEFAULT TO (cDefaultPath)
cDataPath = HOME() + 'Samples\Tastrade\'
*-- 創(chuàng)建示例數(shù)據(jù)表
SELECT First_Name, ;
     Last_Name, ;
     CAST( FILETOSTR(cDataPath + Photo_File ) AS Blob ) AS Photo ;
     FROM ( cDataPath + 'data\Employee.dbf' ) ;
     INTO CURSOR TempCursor
oRL = NEWOBJECT("Photolistener") 
oRL.vPictureVal = "TempCursor.Photo"
SELECT TempCursor
REPORT FORM TestFrx OBJECT oRL
USE IN TempCursor
DEFINE CLASS Photolistener AS ReportListener
     oImage = .NULL.
     vPictureVal = ''
     ListenerType = 1
     PROCEDURE INIT
         THIS.oImage = CREATEOBJECT("Image")
         THIS.oImage.PictureVal = THIS.vPictureVal
     ENDPROC
     PROCEDURE ImageValue
         THIS.oImage.PictureVal = EVALUATE(THIS.vPictureVal) && 此處刷新字段內(nèi)容
     ENDPROC
ENDDEFINE

8. MapVarchar屬性
    Varchar也是VFP9新增加的數(shù)據(jù)類型,以前如果我們后臺是SQL Server, SQL Server里有varchar字段類型,如果我們在后臺設(shè)置了varchar型,可在前臺CursorAdapter里只能映射為Char型,所以后臺的Varchar優(yōu)點無法直接利用,如果要實現(xiàn),只能設(shè)置oCa.ConversionFunc = "Field1 TRIM, Field2 TRIM.....",這樣既麻煩,又容易出錯,現(xiàn)在正是由于VFP9也有了varchar數(shù)據(jù)類型,這一切將變得非常輕松簡單,將oCa.CursorSchema里原來的(C)型,改成(V)型,然后設(shè)置oCa.MapVarchar = .T.,設(shè)置oCa.UseCursorSchema = .T.即可。


    以上8點是我認(rèn)為是 VFP9 CursorAdapter 類最具人性化和效率化的方面的功能增強(qiáng),下面再說說其他幾個C/S 方面的功能增強(qiáng)。


1. RecordsFetched 和 FetchIsComplete

    以前我們讀取大批量記錄的時候,如果想要做一個人性化的進(jìn)度條,是一件非常不容易的事情,一般只能采取異步方式,先讀取記錄總數(shù),然后用一個循環(huán),
lnResult = SQLEXEC(lnHandle,“SELECT * FROM …. WHERE …”,“Temp”)
DO WHILE lnResult = 0
    liResult = CURSORGETPROP("FetchSize") 
    此處寫入進(jìn)度條代碼
ENDDO


    這樣做,效果是出來了,可是也有缺陷,異步其實變成了同步?,F(xiàn)在 VFP9 在CursorGetProp() 里增加了兩個參數(shù),RecordsFetch 和 FetchIsComplete,意思和使用方法如下:


RecordsFetched 返回讀取遠(yuǎn)程表過程中目前所傳回來的記錄數(shù)目


例如:lnResult = CURSORGETPROP("RecordsFetched")


FetchIsComplete 表示讀取過程是否已經(jīng)完成
如果已完成 = .T. 
例如:llResult = CURSORGETPROP("FetchIsComplete")


有了這兩個屬性,要做一個進(jìn)度條就輕而易舉了,而且能實現(xiàn)異步功能不喪失的效果。


RecordsFetch 和 FetchIsComplete應(yīng)該還有其他用處,留待我們一起來慢慢挖掘吧。

2. ASQLHANDLES( ) 函數(shù)

    VFP9以前要想得到當(dāng)前運行程序里使用了多少句柄,將時件非常不容易的事情,VFP9里這又變成了一件輕松簡單的活。

LOCAL lnHandleSum, lnI
LOCAL ARRAY laHandle[1]
lnHandleSum = ASQLHANDLES(laHandle)
'共有連接:'+ TRANSFORM(lnHandleSum)
'連接句柄分別為:'
FOR lnI = 1 TO lnHandleSum
   laHandle(lnI)
ENDFOR


3. SQLEXEC( )里增加了一個aCountInfo參數(shù)

    VFP9以前,當(dāng)我們向后臺發(fā)送Insert,Update,Delete命令以后,要想知道到底執(zhí)行了幾條記錄。如果后臺是SQL Server,只能通過再發(fā)送一條SPT命令lnResutl= SQLEXEC(lnHandle,"select @@ROWCOUNT", lcTemp),然后再通過臨時表來取得,非常麻煩。VFP9里,SQLEXEC()函數(shù)增加了這個參數(shù),
SQLEXEC(nStatementHandle [, cSQLCommand [, cCursorName[, aCountInfo]]])
aCountInfo是一個數(shù)組,包含了向后臺發(fā)送SPT命令所影響的記錄總數(shù)。

4. SQLIDLEDISCONNECT()  這是我認(rèn)為 VFP9 中極其有意義的 C/S 功能增強(qiáng)。

    尤其是對CursorAdapter,當(dāng)然對SPT和遠(yuǎn)程視圖也同樣適用。


    SQLIDLEDISCONNECT() 從字面上理解是暫時中斷連接。剛開始,我始終未能理解其意思,可當(dāng)親手做了實驗以后,就不得不驚嘆其功能之強(qiáng)大了。


    以前在VFP8下我曾仔細(xì)做過 CursorAdapter 的斷線重連的測試,得出結(jié)論,CursorAdapter斷線重連以后,當(dāng)前數(shù)據(jù)可以向后臺更新,但已經(jīng)無法CursorRefresh()了,只能重新CursorFill()以后才行,但重新CursorFill()以后,前臺的Cursor 等于是重新生成了,這對前臺如果是用GRID來綁定 Cursor 時尤為不便。


    現(xiàn)在終于盼到了----- SQLIDLEDISCONNECT(),當(dāng)CursorFill()、CursorRefresh()或者RecordRefresh()以后 SQLIDLEDISCONNECT(lnHandle),此時檢查服務(wù)器,確實此句柄已經(jīng)不存在了,和服務(wù)器 完全脫離了關(guān)系,為了保險起見,把網(wǎng)線拔了,過一段時間再插上,再對CursoraAdapter 進(jìn)行數(shù)據(jù)更新,提交后臺,然后在前臺執(zhí)行CursorRefresh()或者RecordRefresh(),這個時候檢查服務(wù)器,連接句柄自動建立成功了,而且數(shù)據(jù)確實更新成功并重新刷新。


    再用SPT 做了測試,同樣得到了完美的結(jié)果。


    綜上所述,一旦執(zhí)行了SQLIDLEDISCONNECT(),這個句柄就處于休眠狀態(tài),此時完全和服務(wù)器脫離了關(guān)系,一旦發(fā)生前臺向后臺請求任何的任務(wù),此時 VFP 將無需任何條件、無需任何人工干預(yù)的自動喚醒此連接,且句柄號不變。


    此項功能在用戶數(shù)較多或者網(wǎng)絡(luò)環(huán)境較差的情況下尤其有用,特別是在降低服務(wù)器的資源消耗方面有著非常大的作用。


后記: 當(dāng)初VFP9 Beta發(fā)布的時候,我匆匆寫下了此文,其中有很多地方未經(jīng)過深入細(xì)致的測試,現(xiàn)在VFP9正式版已經(jīng)發(fā)布,經(jīng)過在實踐應(yīng)用中發(fā)現(xiàn)很多以前認(rèn)識不夠或錯誤的地方,今天終于抽空重新改寫了此文,希望以前的一些認(rèn)識不夠深入之處未對其他狐友造成誤導(dǎo)!

    在此過程中受到動感游標(biāo)狐友:dupeiji、edwin7521、chxking、net_steven的大力幫助,在此一并謝過。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多