国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 開發(fā) > 綜合 > 正文

Visual Foxpro中的多用戶及數(shù)據(jù)緩沖問(wèn)題

2024-07-21 02:05:53
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
  • 本文來(lái)源于網(wǎng)頁(yè)設(shè)計(jì)愛(ài)好者web開發(fā)社區(qū)http://www.html.org.cn收集整理,歡迎訪問(wèn)。

  • 主題:visual foxpro中的多用戶及數(shù)據(jù)緩沖問(wèn)題

    紹 

    很多狐友(foxers)都是從dbase―foxbase―foxpro―vfp這樣一條路走過(guò)來(lái)的,如果說(shuō)從foxbase 到foxpro是一次飛躍,那么從foxpro到vfp就是一次升華。漫漫編程路上的兩次大變化都伴隨著升級(jí)的興奮與適應(yīng)的痛苦,慣性思維往往使我們?nèi)菀缀雎孕掳姹镜男聝?nèi)容。我們先來(lái)看看下面這個(gè)在表單中編輯記錄的例子:

    在foxpro 2.x時(shí)代,我是這樣設(shè)計(jì)的:

    1、   在屏幕上放置與表中字段對(duì)應(yīng)的文本框(text)控件,用來(lái)存放相應(yīng)的內(nèi)存變量(如m.cust_idt和m.name等);

    2、   當(dāng)用戶將表的指針定位到某一特定記錄時(shí)(比如按了"下一個(gè)記錄"按鈕),就用scatter memvar 語(yǔ)句把該記錄的所有字段傳給相應(yīng)的內(nèi)存變量,再用show get來(lái)刷新屏幕上的文本框中顯示的值。在這一時(shí)刻,用戶是不能編輯這些變量的(此時(shí)文本框或者被設(shè)置為不可用(disabled),或者其when子句返回的是.f.值),因?yàn)橛脩籼幵冢g覽"狀態(tài)。

    3、   當(dāng)用戶選擇了"編輯"按鈕時(shí),程序就鎖定該記錄(如果無(wú)法鎖定,則顯示提示信息),然后檢測(cè)每個(gè)字段值和相應(yīng)的內(nèi)存變量值(如果有不同的,說(shuō)明一定是有其它用戶在我們進(jìn)入編輯狀態(tài)后修改并保存了記錄,在這種情況下,要顯示提示信息),之后再用scatter memvar和show get語(yǔ)句刷新,這樣用戶就看到記錄中的當(dāng)前值了;

    4、   如果所有字段和相應(yīng)的內(nèi)存變量完全匹配,就把變量所在的文本框控件都設(shè)置為可用(enabled)或都讓when子句返回.t.,以便讓用戶可以來(lái)編輯這些變量;

    5、   當(dāng)用戶選擇了"保存"按鈕時(shí),根據(jù)一定的規(guī)則對(duì)輸入的數(shù)據(jù)進(jìn)行檢驗(yàn),然后用gatter memvar語(yǔ)句將內(nèi)存變量寫回到記錄中,并對(duì)記錄解鎖(unlock),再把所有的內(nèi)存變量所在文本框設(shè)置為不可用(disabled),或者讓其when子句返回.f.值,這時(shí),用戶又回到了"瀏覽"狀態(tài)。

    請(qǐng)注意,在這一過(guò)程中我們沒(méi)有直接讀寫記錄,而是先將各字段值賦給同名內(nèi)存變量,再讓用戶編輯這些內(nèi)存變量,如果一切正常再把這些內(nèi)存變量寫回到記錄當(dāng)中。之所以用這種方法主要是為了保護(hù)表,如果不符合驗(yàn)證規(guī)則,就不允許數(shù)據(jù)回存到表中。另一點(diǎn)要注意的就是,當(dāng)用戶編輯記錄時(shí)要對(duì)記錄加鎖(lock),這樣就防止了其它用戶在同一時(shí)間編輯同一條記錄。但是,這種方法有一個(gè)很大的缺點(diǎn):假設(shè)一個(gè)用戶開始編輯某一記錄,在他按下"保存"按鈕前,記錄一直處于鎖定狀態(tài),如果此時(shí)該用戶有事暫時(shí)外出,如午餐,那么其它用戶就不能對(duì)該記錄進(jìn)行編輯了l。

    當(dāng)然,你可以不在進(jìn)入"編輯"狀態(tài)時(shí)加鎖,而只在"保存"記錄之前加鎖,保存完后馬上解鎖,這樣可以使記錄被鎖定時(shí)間最短,以便其它用戶有充分的時(shí)間編輯該記錄。但是這也有缺點(diǎn),試想:如果用戶編輯了內(nèi)存變量,這后點(diǎn)擊"保存",可如果其它用戶在你點(diǎn)"保存"之前也編輯了這條記錄,并且還沒(méi)保存,這時(shí)會(huì)發(fā)生什么現(xiàn)象呢?你的保存要覆蓋他人的修改嗎?要放棄你的修改嗎?這些都是我們要在設(shè)計(jì)時(shí)認(rèn)真考慮的問(wèn)題。

    我們?nèi)绱速M(fèi)盡心機(jī)地設(shè)計(jì),其目的都是為了保護(hù)數(shù)據(jù)。如果你寫的程序只是你一人使用,那設(shè)計(jì)起來(lái)可能會(huì)簡(jiǎn)單得多:你可以直接讀取記錄中的字段,比如你可以直接在屏幕中browse一個(gè)表,這樣你輸入的內(nèi)容就直接寫進(jìn)記錄了。但是,我們不能擔(dān)保那些最終用戶也象您一樣都清楚能輸入什么不能輸入什么,我們不得不在用戶與數(shù)據(jù)表之間建立一個(gè)"防火墻"來(lái)保護(hù)數(shù)據(jù)。在foxpro 2.x中創(chuàng)建這樣的"防火墻"要寫一大堆的代碼!

    值得我們高興的是,vfp提供了內(nèi)建的"防火墻"機(jī)制,它有兩方面的作用:一是可直接讀取記錄,二是只允許通過(guò)所有檢驗(yàn)規(guī)則的數(shù)據(jù)被寫回。這一機(jī)制就是緩沖。

    緩沖

    在剛才我們講到的例子中,是通過(guò)內(nèi)存變量存貯記錄內(nèi)容,這種方法可以被認(rèn)為是手工建立了一個(gè)數(shù)據(jù)緩沖器,通過(guò)使用scatter memvar把數(shù)據(jù)從記錄中傳送到"緩沖"中,再用gather memvar從"緩沖"中傳回到記錄。

    vfp不僅能自動(dòng)進(jìn)行單條記錄的緩沖(稱為行緩沖或記錄緩沖),而且還支持另一種類型的緩沖,即表緩沖,表緩沖可通過(guò)緩沖器存取多條記錄。行緩沖一般用于一次存取一個(gè)記錄時(shí),這種機(jī)制普遍應(yīng)用于數(shù)據(jù)錄入,就象前文提到的那樣:用戶可以在表單中顯示或編輯單條記錄。表緩沖則適用于一次更新多條記錄,比如一張訂貨單的明細(xì)錄入屏幕,通過(guò)對(duì)物品明細(xì)表使用表緩沖,可以允許用戶編輯多條明細(xì)記錄,最后一次性地將所有明細(xì)記錄保存或放棄。

    除了兩種緩沖機(jī)制,vfp還有兩種鎖定機(jī)制。前文講述的那種在foxpro 2.x中的加鎖方式被稱為保守式鎖定法(或悲觀鎖定法)――當(dāng)用戶選擇"編輯"時(shí)加鎖,直到用戶選擇了"保存"后再解鎖。這種加鎖機(jī)制確保了當(dāng)本用戶修改記錄時(shí)其它用戶都不能修改該記錄,但這樣做有利有弊,視具體情況而定。前文所講的另一種加鎖方式叫做開放式加鎖法(或樂(lè)觀鎖定法)――只有在寫回記錄時(shí)才鎖定該記錄,然后馬上解鎖。這種加鎖機(jī)制雖然使記錄最大時(shí)間內(nèi)可給別的用戶使用,但我們必須處理當(dāng)兩個(gè)用戶同時(shí)編輯記錄時(shí)所造成的沖突。正如我們下面將要看到的,這對(duì)于vfp來(lái)說(shuō)真是太簡(jiǎn)單了,在vfp中大多數(shù)的情況下都選用開放式緩沖機(jī)制。

    因?yàn)樵趘fp中記錄可以被自動(dòng)緩沖,所以就不必再用"人工緩沖"機(jī)制了,換句話說(shuō),現(xiàn)在我們可以直接讀記錄中的字段而不必關(guān)心為每個(gè)字段設(shè)內(nèi)存變量。要保存修改,我們只要簡(jiǎn)單地告訴vfp將緩沖器中的內(nèi)容寫到表中即可,若是取消修改,告訴vfp不寫入即可。過(guò)會(huì)兒我們將看到這是如何操作的。

    當(dāng)打開一個(gè)表時(shí),vfp創(chuàng)建一個(gè)"臨時(shí)表"(cursor)作為緩沖器,這個(gè)"臨時(shí)表"用來(lái)定義表的屬性。對(duì)于本地表來(lái)說(shuō),"臨時(shí)表"的唯一屬性是用來(lái)定義緩沖方式的的,視圖和遠(yuǎn)程表還有一些本文討論范圍之外的其它屬性,這些屬性的值用cursorsetprop()函數(shù)設(shè)置,用cursorgetprop()取得。我們過(guò)會(huì)兒將看到如何使用這些函數(shù)。

    當(dāng)追加記錄時(shí),表緩沖有一個(gè)有趣的特性:隨著記錄添加到緩部器中,它們被賦予一個(gè)負(fù)記錄號(hào),第一個(gè)加入的記錄,recno()返回值為-1,第二個(gè)返回值為-2,依此類推。你可以用一個(gè)帶負(fù)數(shù)的go命令在緩沖中定位到追加的記錄上。這在處理記錄號(hào)時(shí)很有用,比如為了確認(rèn)變量inrecno是否為一個(gè)可用的記錄號(hào),在緩沖狀態(tài)下,要檢測(cè)between(inrecno,1,reccount()) or inrecno<0 而不能只用between(inrecno,1,reccount())。

    使用緩沖

    缺省情況下緩沖器是關(guān)閉的,這種情況下在更新表時(shí)vfp和foxpro 2.x是一樣的,要使用緩沖你必需將它打開。緩沖適用于自由表及數(shù)據(jù)庫(kù)中的表。要用緩沖還要set multilocks on,因?yàn)閙ultilocks缺省值是off,如果你忘了設(shè)置其值為on,會(huì)出現(xiàn)錯(cuò)誤信息。你可以把multilocks=on加入到config.fpw文件中,或是用"工具"欄下的"選項(xiàng)"功能保存其值為on。

    我們通過(guò)cursorsetprop(‘buffering’,<n>,<alias>)來(lái)定義緩沖方式。如果是對(duì)當(dāng)前表設(shè)置緩沖,不必定義<alias>,根據(jù)你想要的緩沖及鎖定方式<n>為下列值: 

    緩沖及鎖定法
    <n>值

    無(wú)緩沖
    1

    保守式行緩沖
    2

    開放式行緩沖
    3

    保守式表緩沖
    4

    開放式表緩沖
    5


    例如,要將當(dāng)前表設(shè)置為開放式行緩沖,用cursorsetprop(‘buffering’,3),要取得當(dāng)前正打開的表的緩沖方式,用cursorgetprop(‘buffering’)。

    要設(shè)置一個(gè)表單的緩沖方式,你可以在表單的load事件中用cursorsetprop()定義所有用到的表,但最好的方法是直接設(shè)置表單的buffermode屬性來(lái)決定是開放式還是保守式(缺省值為"無(wú)"),這樣設(shè)置后,該表單就會(huì)自動(dòng)對(duì)綁定到網(wǎng)格(grid)中的表使用表緩沖,對(duì)其它表則使用行緩沖。如果你的表單使用了數(shù)據(jù)環(huán)境,你可以對(duì)某一個(gè)表的buffermodeoverride屬性按你的意圖設(shè)置緩沖,以取代表單的buffermode屬性。

    如果有用戶正在修改緩沖記錄中的數(shù)據(jù)(此時(shí)用戶處在"編輯"狀態(tài)),你不僅可以取得他們輸入到每個(gè)字段中的值,還能取得每個(gè)字段的初始值和當(dāng)前值(此值為磁盤中的實(shí)際值),為此vfp提供了oldval()和curval()函數(shù)。 

    要得到:
    使用:

    用戶輸入值(緩沖的數(shù)據(jù))
    <fieldname> or <alias.fieldname>

    用戶未做任何改動(dòng)之前的值
    oldval(‘<fieldname>’)

    當(dāng)前記錄中的值
    curval(‘<fieldname>’)


    注: curval()和oldval()僅用于開放式緩沖。

    你可能搞不懂curval()返回值和oldval()返回值有什么不同,如果是在單用戶程序中,二者之間是沒(méi)什么區(qū)別,但是如果是在網(wǎng)絡(luò)中,在開放式鎖定下,很可能在本用戶編輯一條記錄時(shí),另一個(gè)用戶也編輯同一條記錄,并在本用戶"保存"之前進(jìn)行了"保存",下面是一個(gè)例子:

    鄭某將contacts.dbf指針定位到第2條記錄,并點(diǎn)擊了"編輯"按鈕: 

    字段
    緩沖值
    初始值oldval()
    當(dāng)前值curval()

    姓名
    李達(dá)
    李達(dá)
    李達(dá)

    公司名稱
    狐友技術(shù)開發(fā)公司
    狐友技術(shù)開發(fā)公司
    狐友技術(shù)開發(fā)公司


    然后,鄭某將公司名稱改為"狐友俱樂(lè)部",但還沒(méi)有保存記錄: 

    字段
    緩沖值
    初始值oldval()
    當(dāng)前值curval()

    姓名
    李達(dá)
    李達(dá)
    李達(dá)

    公司名稱
    狐友俱樂(lè)部
    狐友技術(shù)開發(fā)公司
    狐友技術(shù)開發(fā)公司


    就在此時(shí),于某也將contacts.dbf指針定位到第2條記錄,并點(diǎn)擊了"編輯"按鈕,他改變了公司名稱為"獵狐者俱樂(lè)部",并保存。此時(shí)在鄭某的機(jī)器上會(huì)看到如下結(jié)果: 

    字段
    緩沖值
    初始值oldval()
    當(dāng)前值curval()

    姓名
    李達(dá)
    李達(dá)
    李達(dá)

    公司名稱
    狐友俱樂(lè)部
    狐友技術(shù)開發(fā)公司
    獵狐者俱樂(lè)部


    注意:在上表中contacts.公司名稱、oldval(‘公司名稱’)以及curval(‘公司名稱’)將返回不同的值。訪問(wèn)記錄中各字段的初始值、緩沖值和當(dāng)前值,你可以:

    l      通過(guò)比較緩沖值和初始值來(lái)確定哪些字段被用戶修改了;

    l      通過(guò)比較初始值和當(dāng)前值來(lái)檢測(cè)在開始編輯后,網(wǎng)絡(luò)中是否有其它用戶修改了同一條記錄。 如果你不關(guān)心初始值和當(dāng)前值,而只是希望檢測(cè)到某個(gè)字段中的內(nèi)容是否被修改過(guò),可以用getfldstate()函數(shù)。這個(gè)函數(shù)返回一個(gè)數(shù)值,指出當(dāng)前記錄是否被做了修改。getfldstate()按以下格式調(diào)用:

    getfldstate( <fieldname> | <fieldnumber> [ , <alias> | <workarea> ] )

    返回值及其意義如下表所示: 

    返回值
    意義

    1
    沒(méi)改變

    2
    字段被編輯或者記錄的刪除標(biāo)記被改變

    3
    添加了一條新記錄但沒(méi)編輯字段,以及記錄的刪除記錄未改變

    4
    添加了一條新記錄并編輯了字段,或者記錄的刪除標(biāo)記被改變


    記錄的刪除標(biāo)記被改變,是指刪除記錄或恢復(fù)(recall)記錄。值得注意的是:對(duì)記錄刪除后又馬上進(jìn)行了恢復(fù),盡管對(duì)記錄來(lái)說(shuō)沒(méi)影響,但是其刪除標(biāo)記被改變過(guò),因此,getfldstate()函數(shù)會(huì)返回2或4。

    如果你沒(méi)有定義別名或工作區(qū),getfldstate()將對(duì)當(dāng)前打開的表進(jìn)行操作。將<fieldnumber>定義為0,該函數(shù)返回當(dāng)前記錄的添加及刪除狀態(tài),如果定義為-1,將返回一個(gè)字符串,在這個(gè)字符串中,第一個(gè)數(shù)字反映整個(gè)表的狀態(tài),以后每個(gè)數(shù)字返映的是各字段的狀態(tài)。

    以我們前面講到的情況為例,在鄭某編輯第2條記錄時(shí),getfldstate(-1)將返回"112",第一個(gè)數(shù)字"1"說(shuō)明記錄沒(méi)有添加或刪除,第二個(gè)數(shù)字"1"說(shuō)明第一個(gè)字段(姓名)沒(méi)有改變,第三個(gè)數(shù)字"2"說(shuō)明第二個(gè)字段(公司名稱)內(nèi)容改變了。

    緩沖記錄的寫回

    我們還繼續(xù)剛才的例子。現(xiàn)在假設(shè)鄭某點(diǎn)擊了"保存"按鈕,我們應(yīng)該怎樣將緩沖中的數(shù)據(jù)寫到記錄中(更新表)呢?對(duì)于行緩沖來(lái)說(shuō),當(dāng)你移動(dòng)記錄指針或調(diào)用tableupdate()函數(shù)時(shí),表就會(huì)被更新。對(duì)于表緩沖來(lái)說(shuō),移動(dòng)記錄指針并不會(huì)引起表的更新(因?yàn)樗嵌嘤涗洷煌瑫r(shí)緩沖),所以通常情況下只能調(diào)用tableupdate()函數(shù)來(lái)更新表。對(duì)于行緩沖最好也用tableupdate()函數(shù),因?yàn)檫@樣更好地控制程序的去向。

    如果緩沖器中的內(nèi)容被正確地寫入到記錄中,tableupdate()返回.t.值,如果記錄緩沖沒(méi)有改變(用戶沒(méi)有編輯任何字段、添加記錄或改變記錄的刪除狀態(tài)),此時(shí),tableupdate()也返回.t.,盡管實(shí)際上什么也沒(méi)有做。

    tableupdate()可以帶幾個(gè)參數(shù):

    tableupdate( <allrows>,<forced>,<alias>|<workarea> )

    第一個(gè)參數(shù)指明哪些記錄被更新:設(shè)為.f.,則只更新當(dāng)前記錄,若為.t.,則更新所有記錄(僅影響表緩沖)。如果第二個(gè)參數(shù)是.t.,那么其它用戶的任何修改將被當(dāng)前用戶的修改所覆蓋。如果沒(méi)定義第三個(gè)參數(shù),tableupdate()將更新當(dāng)前表。

    怎樣取消用戶所做的修改呢?對(duì)于用內(nèi)存變量的方法,可以再次用scatter memevar 語(yǔ)句從磁盤上的數(shù)據(jù)恢復(fù)到內(nèi)存變量中,而對(duì)于緩沖來(lái)說(shuō),用tablerevert()函數(shù)即可達(dá)到同樣功能。

    錯(cuò)誤處理

    我們繼續(xù)"鄭某和于某"的例子,當(dāng)鄭某點(diǎn)擊"保存"按鈕后,代碼將執(zhí)行tableupdate()函數(shù)以把緩沖中的數(shù)據(jù)寫入記錄。請(qǐng)記住,在鄭某編輯記錄時(shí)于某已經(jīng)修改了同一條記錄并做了保存。當(dāng)鄭某點(diǎn)擊"保存"時(shí),tableupdate() 將返回.f.,說(shuō)明它不能將緩沖寫入記錄中,為什么會(huì)是這樣呢?

    vfp在以下幾種情況下無(wú)法將緩沖寫入記錄:

    l      當(dāng)一個(gè)用戶編輯記錄時(shí),其它用戶修改并保存了該記錄(正如我們例子中的那種情況)。vfp自動(dòng)對(duì)每個(gè)字段的oldval()值和curval()值進(jìn)行比較,如果檢測(cè)到任何不同,就會(huì)產(chǎn)生沖突。

    l      用戶輸入了重復(fù)的主索引或候選索引值。

    l      違背了某個(gè)字段或表的驗(yàn)證規(guī)則,或者不支持null的字段出現(xiàn)了null值。

    l      某個(gè)觸發(fā)(trigger)失敗。

    l      其它用戶鎖定了該記錄。

    l      其它用戶刪除了該記錄。

    當(dāng)tableupdate()失敗時(shí),我們必須決定下一步做什么,而且,如果你的程序在編輯記錄時(shí)允許用戶點(diǎn)擊"下一個(gè)"或"上一個(gè)"按鈕,而這兩個(gè)按鈕中又沒(méi)有調(diào)用tableupdate()的話,你必須得處理在自動(dòng)保存時(shí)將會(huì)發(fā)生的錯(cuò)誤。在這兩種情況下,將程序指定到適當(dāng)?shù)奈恢镁褪清e(cuò)誤陷阱處理程序。

    visual foxpro中的多用戶及數(shù)據(jù)緩沖問(wèn)題(下)

    --------------------------------------------------------------------------------

    2000-10-6 17:07:00


    在vfp中錯(cuò)誤處理已經(jīng)得到改進(jìn)。以前處理錯(cuò)誤陷阱的方法(你仍然可以在vfp中繼續(xù)使用這些方法)是當(dāng)錯(cuò)誤發(fā)生時(shí)用on error命令來(lái)決定要執(zhí)行的程序,典型的錯(cuò)誤處理程序是查看error()和message()來(lái)確定發(fā)生了什么錯(cuò)誤,然后采取相應(yīng)的動(dòng)作。

    現(xiàn)在vfp提供了一種自動(dòng)的錯(cuò)誤處理機(jī)制:就是error方法。如果定義了一個(gè)控件或表單中的error方法,當(dāng)錯(cuò)誤發(fā)生時(shí)它就被自動(dòng)執(zhí)行。aerror()是vfp的一個(gè)新增函數(shù),通過(guò)傳遞一個(gè)參數(shù),該函數(shù)可以創(chuàng)建或更新一個(gè)含有以下元素的數(shù)組

    元素
    類型
    描述

    1
    數(shù)字
    錯(cuò)誤號(hào)(與error()相同)

    2
    字符
    錯(cuò)誤信息(與message()相同)

    3
    字符
    如果有一個(gè)錯(cuò)誤信息參數(shù)(與sys(2018)相同),則返回之(例如:一個(gè)字段名),無(wú),則返回.null.

    4
    數(shù)字或字符
    發(fā)生錯(cuò)誤的工作區(qū)。如果沒(méi)有,則返回.null.

    5
    數(shù)字或字符
    如果一個(gè)觸發(fā)器失敗,返回觸發(fā)器號(hào)(插入為1,更新為2,刪除為3),如果沒(méi)有則返回.null.

    6
    數(shù)字或字符
    .null.(應(yīng)用于ole和odbc錯(cuò)誤)

    7
    數(shù)字
    .null.(應(yīng)用于ole錯(cuò)誤)


    例如:aerror(iaerror)會(huì)創(chuàng)建或更新一個(gè)稱為iaerror的數(shù)組。

    以下是vfp在將緩沖寫入表時(shí)可能發(fā)生的一些錯(cuò)誤:

    錯(cuò)誤號(hào)#
    錯(cuò)誤信息
    說(shuō)明

    109
    記錄正由其它用戶使用


    1539
    觸發(fā)器失敗
    檢測(cè)數(shù)組的第5個(gè)元素可以確定是哪個(gè)觸發(fā)器失敗了

    1581
    字段不接受空值(null)
    檢測(cè)數(shù)組的第3個(gè)元素可以確定是哪個(gè)字段引起的錯(cuò)誤

    1582
    違反了字段的驗(yàn)證規(guī)則
    檢測(cè)數(shù)組的第3個(gè)元素可以確定是哪個(gè)字段引起的錯(cuò)誤

    1583
    違反了記錄的驗(yàn)證規(guī)則


    1585
    記錄已被其它用戶修改


    1884
    違反了索引的唯一性
    檢測(cè)數(shù)組的第3個(gè)元素可以確定是哪個(gè)索引標(biāo)記引起的錯(cuò)誤


    對(duì)于以上這些錯(cuò)誤,大部分可以直接處理:提示用戶問(wèn)題所在,然后讓用戶在"編輯"狀態(tài)下改正錯(cuò)誤或是取消操作。對(duì)于#1585錯(cuò)誤(記錄已被其它用戶修改),有以下幾種處理錯(cuò)誤的方法:

    l 可以提示當(dāng)前用戶有別人修改了該記錄,然后用tablerevert()取消當(dāng)前用戶的編輯內(nèi)容。我想多數(shù)人對(duì)這種方法會(huì)不高興的。

    l 可以用tableupdate(.f. , .t.)來(lái)強(qiáng)行更新記錄,使當(dāng)前用戶的修改覆蓋其它用戶修改。這樣做,當(dāng)前用戶自然是高興的,但其它用戶可能就要不滿了。:(

    l 復(fù)制一個(gè)相同的表單(在vfp中對(duì)同一表單創(chuàng)建多個(gè)實(shí)例是非常容易的),在上面顯示出其它用戶對(duì)該記錄的修改,這樣,當(dāng)前用戶就可以決定是保存還是不保存其它用戶的修改,也就是說(shuō),可以用tableupdate(.f. , .t.)強(qiáng)行更新或是用tablerevert()來(lái)取消編輯。

    有更好的方案來(lái)檢測(cè)我們是否遇到了"真正的"沖突,所謂"真正的"沖突,是指兩個(gè)用戶在同一時(shí)間修改了同一個(gè)字段。如果他們修改的是同一記錄的兩個(gè)不同字段,我們可以只更新當(dāng)前用戶修改的字段,而保留另一個(gè)用戶修改的字段,使之不受影響。例如,在一個(gè)訂單處理系統(tǒng)中,一個(gè)用戶修改了產(chǎn)品介紹,而同時(shí)另一個(gè)用戶在對(duì)該產(chǎn)品下訂單,正在輸入數(shù)量,這兩個(gè)修改相互獨(dú)立,并沒(méi)有沖突,這時(shí)我們不要一次更新整條記錄,而是只更新自己修改的那個(gè)字段,如此一來(lái),兩個(gè)用戶都會(huì)感到滿意。

    以下是實(shí)現(xiàn)這一想法的思路:

    l 查找oldval()與curval()不同的字段,如果有,說(shuō)明該字段已被其它用戶編輯過(guò),如果該字段的緩沖值與oldval()相同,說(shuō)明當(dāng)前用戶并沒(méi)有修改該字段。這種情況下,我們可以將curval()中的值先傳給緩沖值,再更新,這樣就可以避免緩沖值覆蓋新值了。

    l 查找緩沖值與oldval()不同的字段,這些字段是當(dāng)前用戶修改過(guò)的字段,如果oldval()又等于curval(),說(shuō)明其它用戶沒(méi)有改動(dòng)過(guò)該字段,這種情況下,我們可以放心地覆蓋掉它。

    l 如果我們找到的字段,其緩沖值與oldval()不同,但與curval()相同,這說(shuō)明兩個(gè)用戶對(duì)同一字段做了相同的修改。這種情況看起來(lái)好象不大可能,其實(shí)是會(huì)發(fā)生的。比如有人發(fā)來(lái)了一個(gè)公司的地址變動(dòng)信息,而恰恰有兩個(gè)用戶同時(shí)決定據(jù)此來(lái)更新記錄中的公司地址。因?yàn)樗麄兯龅男薷膬?nèi)容是相同的,我們可以覆蓋另一個(gè)即可。但是,如果是以同一個(gè)數(shù)量更新一個(gè)量值的話(比如兩人同時(shí)下訂單,且輸入了相同的數(shù)量),你就不能簡(jiǎn)單地覆蓋字段,應(yīng)該將此看作為"真正的"沖突。

    l 如果發(fā)現(xiàn)一個(gè)字段的緩沖值既不同于oldval()也不同于curval(),而且oldval()與curval()也各不相同,這說(shuō)明兩個(gè)用戶都修改了同一個(gè)字段,且值不相同。這種情況才是我們遇到的真正的沖突,我們不得不決定怎樣處理這一沖突。

    在手工輸入存貨數(shù)量或帳目余額時(shí),有一種可能就是其它用戶輸入的值對(duì)緩沖值會(huì)產(chǎn)生影響。比如,如果檢測(cè)到oldval()是10, curval()是20,說(shuō)明其它用戶將數(shù)額已增加了10;如果現(xiàn)在緩沖值為5,說(shuō)明當(dāng)前用戶要將數(shù)額在原基礎(chǔ)(10)上減少5,因此新的緩沖值就應(yīng)該是value+ oldval()- curval(),即等于15,應(yīng)該用此數(shù)來(lái)更新字段。

    對(duì)于日期型字段的沖突,就要考慮商業(yè)規(guī)則或按實(shí)際情況處理。例如,在"病人預(yù)約時(shí)間"程序中,有一個(gè)字段保存著病人下次預(yù)約醫(yī)生的時(shí)間,如果該字段兩個(gè)日期發(fā)生沖突的話,那么,靠近當(dāng)前日期的那個(gè)日期很可能是正確的。當(dāng)然,若是其中一個(gè)預(yù)約日期比當(dāng)前日期還要早的話(已經(jīng)過(guò)期),那就應(yīng)該取靠后的那個(gè)日期了。

    其它類型的字段,特別是字符型和備注型字段,通常情況下如果不詢問(wèn)用戶以決定是覆蓋其它用戶的修改還是取消自己的修改的話,則沖突不好解決。只有當(dāng)用戶在屏幕上看到其它用戶到底做了什么修改后,他才能做出正確地判斷。

    以下是解決沖突的一些代碼(這些代碼假設(shè)已檢測(cè)到錯(cuò)誤碼為#1585,即記錄已被其它用戶修改過(guò)。)

    * 檢測(cè)每一個(gè)字段,看哪個(gè)發(fā)生了沖突。

    llconflict = .f.

    for lni = 1 to fcount()

    lcfield = field(lni)

    llotheruser = oldval(lcfield) <> curval(lcfield)

    llthisuser = evaluate(lcfield) <> oldval(lcfield)

    llsamechange = evaluate(lcfield) == curval(lcfield)

    do case

    * 其它用戶編輯了該字段,而當(dāng)前用戶沒(méi)編輯,所以直接用新值即可。

    case llotheruser and not llthisuser

    replace (lcfield) with curval(lcfield)

    * 其它用戶沒(méi)有編輯該字段,或者二者做了相同的修改,因此我們無(wú)需做任何處理。

    case not llotheruser or llsamechange

    * 兩個(gè)用戶以不同的值修改了該字段。

    otherwise

    llconflict = .t.

    endcase

    next lni

    * 如果發(fā)生了沖突,處理之!

    if llconflict

    lnchoice = messagebox('another user also changed this ' + ;

    'record. do you want to overwrite their changes (yes), ' + ;

    'not overwrite but see their changes (no), or cancel ' + ;

    'your changes (cancel)?', 3 + 16, 'problem saving record!')

    do case

    * 覆蓋其它用戶的修改。

    case lnchoice = 6

    = tableupdate(.f., .t.)

    * 通過(guò)產(chǎn)生一個(gè)表單實(shí)例來(lái)查看其它用戶的修改內(nèi)容。

    case lnchoice = 7

    do form myform name oname

    * 取消當(dāng)前用戶的修改。

    otherwise

    = tablerevert()

    endcase

    * 如果沒(méi)有發(fā)生沖突,則強(qiáng)行更新。

    else

    = tableupdate(.f., .t.)

    endif llconflict

    表緩沖的寫入

    我們前面已經(jīng)講到,可以用tableupdate(.t.)將表緩沖中的所有記錄一次寫入磁盤。與行緩沖一樣,如果其它用戶修改了表(或是其它什么出錯(cuò)原因)而不能正確更新表,tableupdate(.t.)將返回.f.值。

    前文所講的錯(cuò)誤處理程序在行緩沖模式下運(yùn)行良好,因?yàn)槲覀冊(cè)谀骋粫r(shí)刻只關(guān)心單條記錄。但對(duì)于表緩沖來(lái)說(shuō),我們不得不考慮每一條記錄,因?yàn)樵诰彌_區(qū)中可能既有修改過(guò)的記錄,也有未修改過(guò)的記錄,我們?cè)鯓又赖降赘履臈l記錄呢?如果用tableupdate(.t.)失敗(返回.f.),情況就變得更加復(fù)雜化:我們不知道錯(cuò)在哪條記錄上!而且有些記錄可能被做過(guò)"保存",所以還不止一條記錄會(huì)發(fā)生沖突呢。請(qǐng)不要著急:),vfp新增函數(shù)getnextmodified()可以精確地告訴我們想知道的信息:該函數(shù)返回下一個(gè)被修改記錄的記錄號(hào)。如果返回值為0,說(shuō)明在緩沖區(qū)中沒(méi)有被修改過(guò)的記錄。這個(gè)函數(shù)接收兩個(gè)參數(shù):第一個(gè)參數(shù)是一個(gè)記錄號(hào),正是從這個(gè)記錄號(hào)開始向下查找下一個(gè)被修改的記錄;第二個(gè)參數(shù)是查找的工作區(qū)別名。最被,你應(yīng)該將0傳給第一個(gè)參數(shù),這樣getnextmodified()就會(huì)找到第一個(gè)被修改的記錄,若要繼續(xù)找下一個(gè)被修改的記錄,只要將當(dāng)前記錄的記錄號(hào)傳給第一個(gè)參數(shù)即可。

    下面是在剛才處理沖突的程序基礎(chǔ)上改進(jìn)后的代碼,它用來(lái)處理表緩沖更新失敗時(shí)的操作。

    * 先找到第一個(gè)被修改過(guò)的記錄。

    lnchanged = getnextmodified(0)

    do while lnchanged <> 0

    * 移動(dòng)記錄指針并嘗試鎖定它。

    go lnchanged

    if rlock()

    * 檢測(cè)每一個(gè)字段,看哪個(gè)發(fā)生了沖突。

    llconflict = .f.

    for lni = 1 to fcount()

    lcfield = field(lni)

    llotheruser = oldval(lcfield) <> curval(lcfield)

    llthisuser = evaluate(lcfield) <> oldval(lcfield)

    llsamechange = evaluate(lcfield) == curval(lcfield)

    do case

    * 其它用戶編輯了該字段,而當(dāng)前用戶沒(méi)編輯,所以直接用新值即可。

    case llotheruser and not llthisuser

    replace (lcfield) with curval(lcfield)

    * 其它用戶沒(méi)有編輯該字段,或者二者做了相同的修改,因此我們無(wú)需做任何處理。

    case not llotheruser or llsamechange

    * 兩個(gè)用戶以不同的值修改了該字段。

    otherwise

    llconflict = .t.

    endcase

    next lni

    * 如果發(fā)生了字段沖突,我們可以在此處理它,與行緩沖不同的是,我們也可以現(xiàn)在不處理,因?yàn)橐院笏杏涗泴⒈粚懭耄綍r(shí)會(huì)處理的。

    if llconflict

    lnchoice = messagebox('another user also changed ' + ;

    'record ' + ltrim(str(lnchanged)) + '. do you want to ' + ;

    'overwrite their changes (yes), not overwrite but see ' + ;

    'their changes (no), or cancel your changes (cancel)?', 3 + 16, ;

    'problem saving record!')

    do case

    * 如果選擇了覆蓋其它用戶的修改,在此可以不做處理,因?yàn)橐院髮⒁淮涡愿隆?

    case lnchoice = 6

    *通過(guò)產(chǎn)生一個(gè)表單實(shí)例來(lái)查看其它用戶的修改內(nèi)容。

    &nb

    轉(zhuǎn)載!

     
    發(fā)表評(píng)論 共有條評(píng)論
    用戶名: 密碼:
    驗(yàn)證碼: 匿名發(fā)表
    主站蜘蛛池模板: 榆树市| 界首市| 盱眙县| 广饶县| 开阳县| 辽中县| 遂川县| 武汉市| 安龙县| 广昌县| 平江县| 革吉县| 怀仁县| 雷波县| 云和县| 阿尔山市| 定边县| 蒲城县| 漯河市| 朝阳市| 鄯善县| 开阳县| 怀柔区| 湄潭县| 彰化市| 福建省| 犍为县| 大竹县| 山东| 东港市| 双鸭山市| 武隆县| 高要市| 清远市| 洞口县| 富阳市| 柳河县| 宁津县| 抚宁县| 光泽县| 浦县|