主紹
不少狐友(Foxers)都是從Dbase―FoxBase―Foxpro―VFP這樣一條路走過來的,若是說從FoxBase 到Foxpro是一次飛躍,那麼從Foxpro到VFP就是一次昇華。漫漫編程路上的兩次大變化都伴隨着升級的興奮與適應的痛苦,慣性思惟每每使咱們容易忽略新版本的新內容。咱們先來看看下面這個在表單中編輯記錄的例子:
在Foxpro 2.X時代,我是這樣設計的:
一、 在屏幕上放置與表中字段對應的文本框(TEXT)控件,用來存放相應的內存變量(如M.CUST_IDT和M.NAME等);
二、 當用戶將表的指針定位到某一特定記錄時(好比按了"下一個記錄"按鈕),就用scatter memvar 語句把該記錄的全部字段傳給相應的內存變量,再用show get來刷新屏幕上的文本框中顯示的值。在這一時刻,用戶是不能編輯這些變量的(此時文本框或者被設置爲不可用(Disabled),或者其when子句返回的是.F.值),由於用戶處在"瀏覽"狀態。
三、 當用戶選擇了"編輯"按鈕時,程序就鎖定該記錄(若是沒法鎖定,則顯示提示信息),而後檢測每一個字段值和相應的內存變量值(若是有不一樣的,說明必定是有其它用戶在咱們進入編輯狀態後修改並保存了記錄,在這種狀況下,要顯示提示信息),以後再用scatter memvar和show get語句刷新,這樣用戶就看到記錄中的當前值了;
四、 若是全部字段和相應的內存變量徹底匹配,就把變量所在的文本框控件都設置爲可用(Enabled)或都讓when子句返回.T.,以便讓用戶能夠來編輯這些變量;
五、 當用戶選擇了"保存"按鈕時,根據必定的規則對輸入的數據進行檢驗,而後用gatter memvar語句將內存變量寫回到記錄中,並對記錄解鎖(unlock),再把全部的內存變量所在文本框設置爲不可用(Disabled),或者讓其when子句返回.F.值,這時,用戶又回到了"瀏覽"狀態。
請注意,在這一過程當中咱們沒有直接讀寫記錄,而是先將各字段值賦給同名內存變量,再讓用戶編輯這些內存變量,若是一切正常再把這些內存變量寫回到記錄當中。之因此用這種方法主要是爲了保護表,若是不符合驗證規則,就不容許數據回存到表中。另外一點要注意的就是,當用戶編輯記錄時要對記錄加鎖(lock),這樣就防止了其它用戶在同一時間編輯同一條記錄。可是,這種方法有一個很大的缺點:假設一個用戶開始編輯某一記錄,在他按下"保存"按鈕前,記錄一直處於鎖定狀態,若是此時該用戶有事暫時外出,如午飯,那麼其它用戶就不能對該記錄進行編輯了L。
固然,你能夠不在進入"編輯"狀態時加鎖,而只在"保存"記錄以前加鎖,保存完後立刻解鎖,這樣可使記錄被鎖定時間最短,以便其它用戶有充分的時間編輯該記錄。可是這也有缺點,試想:若是用戶編輯了內存變量,這後點擊"保存",可若是其它用戶在你點"保存"以前也編輯了這條記錄,而且還沒保存,這時會發生什麼現象呢?你的保存要覆蓋他人的修改嗎?要放棄你的修改嗎?這些都是咱們要在設計時認真考慮的問題。
咱們如此費盡心機地設計,其目的都是爲了保護數據。若是你寫的程序只是你一人使用,那設計起來可能會簡單得多:你能夠直接讀取記錄中的字段,好比你能夠直接在屏幕中browse一個表,這樣你輸入的內容就直接寫進記錄了。可是,咱們不能擔保那些最終用戶也象您同樣都清楚能輸入什麼不能輸入什麼,咱們不得不在用戶與數據表之間創建一個"防火牆"來保護數據。在Foxpro 2.X中建立這樣的"防火牆"要寫一大堆的代碼!
值得咱們高興的是,VFP提供了內建的"防火牆"機制,它有兩方面的做用:一是可直接讀取記錄,二是隻容許經過全部檢驗規則的數據被寫回。這一機制就是緩衝。
緩衝
在剛纔咱們講到的例子中,是經過內存變量存貯記錄內容,這種方法能夠被認爲是手工創建了一個數據緩衝器,經過使用scatter memvar把數據從記錄中傳送到"緩衝"中,再用gather memvar從"緩衝"中傳回到記錄。
VFP不只能自動進行單條記錄的緩衝(稱爲行緩衝或記錄緩衝),並且還支持另外一種類型的緩衝,即表緩衝,表緩衝可經過緩衝器存取多條記錄。行緩衝通常用於一次存取一個記錄時,這種機制廣泛應用於數據錄入,就象前文提到的那樣:用戶能夠在表單中顯示或編輯單條記錄。表緩衝則適用於一次更新多條記錄,好比一張定貨單的明細錄入屏幕,經過對物品明細表使用表緩衝,能夠容許用戶編輯多條明細記錄,最後一次性地將全部明細記錄保存或放棄。
除了兩種緩衝機制,VFP還有兩種鎖定機制。前文講述的那種在Foxpro 2.X中的加鎖方式被稱爲保守式鎖定法(或悲觀鎖定法)――當用戶選擇"編輯"時加鎖,直到用戶選擇了"保存"後再解鎖。這種加鎖機制確保了當本用戶修改記錄時其它用戶都不能修改該記錄,但這樣作有利有弊,視具體狀況而定。前文所講的另外一種加鎖方式叫作開放式加鎖法(或樂觀鎖定法)――只有在寫回記錄時才鎖定該記錄,而後立刻解鎖。這種加鎖機制雖然使記錄最大時間內可給別的用戶使用,但咱們必須處理當兩個用戶同時編輯記錄時所形成的衝突。正如咱們下面將要看到的,這對於VFP來講真是太簡單了,在VFP中大多數的狀況下都選用開放式緩衝機制。
由於在VFP中記錄能夠被自動緩衝,因此就沒必要再用"人工緩衝"機制了,換句話說,如今咱們能夠直接讀記錄中的字段而沒必要關心爲每一個字段設內存變量。要保存修改,咱們只要簡單地告訴VFP將緩衝器中的內容寫到表中便可,如果取消修改,告訴VFP不寫入便可。過會兒咱們將看到這是如何操做的。
當打開一個表時,VFP建立一個"臨時表"(cursor)做爲緩衝器,這個"臨時表"用來定義表的屬性。對於本地表來講,"臨時表"的惟一屬性是用來定義緩衝方式的的,視圖和遠程表還有一些本文討論範圍以外的其它屬性,這些屬性的值用CursorSetProp()函數設置,用CursorGetProp()取得。咱們過會兒將看到如何使用這些函數。
當追加記錄時,表緩衝有一個有趣的特性:隨着記錄添加到緩部器中,它們被賦予一個負記錄號,第一個加入的記錄,recno()返回值爲-1,第二個返回值爲-2,依此類推。你能夠用一個帶負數的go命令在緩衝中定位到追加的記錄上。這在處理記錄號時頗有用,好比爲了確認變量InRecno是否爲一個可用的記錄號,在緩衝狀態下,要檢測between(InRecno,1,reccount()) OR InRecno<0 而不能只用between(InRecno,1,reccount())。
使用緩衝
缺省狀況下緩衝器是關閉的,這種狀況下在更新表時VFP和Foxpro 2.X是同樣的,要使用緩衝你必需將它打開。緩衝適用於自由表及數據庫中的表。要用緩衝還要set multilocks on,由於multilocks缺省值是OFF,若是你忘了設置其值爲ON,會出現錯誤信息。你能夠把multilocks=ON加入到CONFIG.FPW文件中,或是用"工具"欄下的"選項"功能保存其值爲ON。
咱們經過cursorsetprop(‘Buffering’,<n>,<Alias>)來定義緩衝方式。若是是對當前表設置緩衝,沒必要定義<Alias>,根據你想要的緩衝及鎖定方式<n>爲下列值:
緩衝及鎖定法
<n>值
無緩衝
1
保守式行緩衝
2
開放式行緩衝
3
保守式表緩衝
4
開放式表緩衝
5 例如,要將當前表設置爲開放式行緩衝,用cursorsetprop(‘Buffering’,3),要取得當前正打開的表的緩衝方式,用cursorgetprop(‘Buffering’)。
要設置一個表單的緩衝方式,你能夠在表單的Load事件中用cursorsetprop()定義全部用到的表,但最好的方法是直接設置表單的BufferMode屬性來決定是開放式仍是保守式(缺省值爲"無"),這樣設置後,該表單就會自動對綁定到網格(Grid)中的表使用表緩衝,對其它表則使用行緩衝。若是你的表單使用了數據環境,你能夠對某一個表的BufferModeOverride屬性按你的意圖設置緩衝,以取表明單的BufferMode屬性。
若是有用戶正在修改緩衝記錄中的數據(此時用戶處在"編輯"狀態),你不只能夠取得他們輸入到每一個字段中的值,還能取得每一個字段的初始值和當前值(此值爲磁盤中的實際值),爲此VFP提供了oldval()和curval()函數。
要獲得:
使用:
用戶輸入值(緩衝的數據)
<fieldname> OR <alias.fieldname>
用戶未作任何改動以前的值
Oldval(‘<fieldname>’)
當前記錄中的值
Curval(‘<fieldname>’) 注: curval()和oldval()僅用於開放式緩衝。
你可能搞不懂curval()返回值和oldval()返回值有什麼不一樣,若是是在單用戶程序中,兩者之間是沒什麼區別,可是若是是在網絡中,在開放式鎖定下,極可能在本用戶編輯一條記錄時,另外一個用戶也編輯同一條記錄,並在本用戶"保存"以前進行了"保存",下面是一個例子:
鄭某將CONTACTS.DBF指針定位到第2條記錄,並點擊了"編輯"按鈕:
字段
緩衝值 初始值Oldval() 當前值Curval()
姓名
李達 李達 李達
公司名稱
狐友技術開發公司 狐友技術開發公司 狐友技術開發公司 而後,鄭某將公司名稱改成"狐友俱樂部",但尚未保存記錄:
字段
緩衝值 初始值Oldval() 當前值Curval()
姓名
李達 李達 李達
公司名稱
狐友俱樂部 狐友技術開發公司 狐友技術開發公司 就在此時,於某也將CONTACTS.DBF指針定位到第2條記錄,並點擊了"編輯"按鈕,他改變了公司名稱爲"獵狐者俱樂部",並保存。此時在鄭某的機器上會看到以下結果:
字段
緩衝值 初始值Oldval() 當前值Curval()
姓名
李達 李達 李達
公司名稱
狐友俱樂部 狐友技術開發公司 獵狐者俱樂部 注意:在上表中CONTACTS.公司名稱、oldval(‘公司名稱’)以及curval(‘公司名稱’)將返回不一樣的值。訪問記錄中各字段的初始值、緩衝值和當前值,你能夠:
l 經過比較緩衝值和初始值來肯定哪些字段被用戶修改了;
l 經過比較初始值和當前值來檢測在開始編輯後,網絡中是否有其它用戶修改了同一條記錄。 若是你不關心初始值和當前值,而只是但願檢測到某個字段中的內容是否被修改過,能夠用getfldstate()函數。這個函數返回一個數值,指出當前記錄是否被作了修改。Getfldstate()按如下格式調用:
getfldstate( <fieldName> | <FieldNumber> [ , <Alias> | <WorkArea> ] )
返回值及其意義以下表所示:
返回值
意義
1
沒改變
2
字段被編輯或者記錄的刪除標記被改變
3
添加了一條新記錄但沒編輯字段,以及記錄的刪除記錄未改變
4
添加了一條新記錄並編輯了字段,或者記錄的刪除標記被改變 記錄的刪除標記被改變,是指刪除記錄或恢復(recall)記錄。值得注意的是:對記錄刪除後又立刻進行了恢復,儘管對記錄來講沒影響,可是其刪除標記被改變過,所以,getfldstate()函數會返回2或4。
若是你沒有定義別名或工做區,getfldstate()將對當前打開的表進行操做。將<FieldNumber>定義爲0,該函數返回當前記錄的添加及刪除狀態,若是定義爲-1,將返回一個字符串,在這個字符串中,第一個數字反映整個表的狀態,之後每一個數字返映的是各字段的狀態。
以咱們前面講到的狀況爲例,在鄭某編輯第2條記錄時,getfldstate(-1)將返回"112",第一個數字"1"說明記錄沒有添加或刪除,第二個數字"1"說明第一個字段(姓名)沒有改變,第三個數字"2"說明第二個字段(公司名稱)內容改變了。
緩衝記錄的寫回
咱們還繼續剛纔的例子。如今假設鄭某點擊了"保存"按鈕,咱們應該怎樣將緩衝中的數據寫到記錄中(更新表)呢?對於行緩衝來講,當你移動記錄指針或調用tableupdate()函數時,表就會被更新。對於表緩衝來講,移動記錄指針並不會引發表的更新(由於它是多記錄被同時緩衝),因此一般狀況下只能調用tableupdate()函數來更新表。對於行緩衝最好也用tableupdate()函數,由於這樣更好地控制程序的去向。
若是緩衝器中的內容被正確地寫入到記錄中,tableupdate()返回.T.值,若是記錄緩衝沒有改變(用戶沒有編輯任何字段、添加記錄或改變記錄的刪除狀態),此時,tableupdate()也返回.T.,儘管實際上什麼也沒有作。
Tableupdate()能夠帶幾個參數:
Tableupdate( <AllRows>,<Forced>,<Alias>|<Workarea> )
第一個參數指明哪些記錄被更新:設爲.F.,則只更新當前記錄,若爲.T.,則更新全部記錄(僅影響表緩衝)。若是第二個參數是.T.,那麼其它用戶的任何修改將被當前用戶的修改所覆蓋。若是沒定義第三個參數,tableupdate()將更新當前表。
怎樣取消用戶所作的修改呢?對於用內存變量的方法,能夠再次用scatter memevar 語句從磁盤上的數據恢復到內存變量中,而對於緩衝來講,用tablerevert()函數便可達到一樣功能。
錯誤處理
咱們繼續"鄭某和於某"的例子,當鄭某點擊"保存"按鈕後,代碼將執行tableupdate()函數以把緩衝中的數據寫入記錄。請記住,在鄭某編輯記錄時於某已經修改了同一條記錄並作了保存。當鄭某點擊"保存"時,tableupdate() 將返回.F.,說明它不能將緩衝寫入記錄中,爲何會是這樣呢?
VFP在如下幾種狀況下沒法將緩衝寫入記錄:
l 當一個用戶編輯記錄時,其它用戶修改並保存了該記錄(正如咱們例子中的那種狀況)。VFP自動對每一個字段的oldval()值和curval()值進行比較,若是檢測到任何不一樣,就會產生衝突。
l 用戶輸入了重複的主索引或候選索引值。
l 違背了某個字段或表的驗證規則,或者不支持null的字段出現了null值。
l 某個觸發(trigger)失敗。
l 其它用戶鎖定了該記錄。
l 其它用戶刪除了該記錄。
當tableupdate()失敗時,咱們必須決定下一步作什麼,並且,若是你的程序在編輯記錄時容許用戶點擊"下一個"或"上一個"按鈕,而這兩個按鈕中又沒有調用tableupdate()的話,你必須得處理在自動保存時將會發生的錯誤。在這兩種狀況下,將程序指定到適當的位置就是錯誤陷阱處理程序。
Visual Foxpro中的多用戶及數據緩衝問題(下)
--------------------------------------------------------------------------------
2000-10-6 17:07:00
在VFP中錯誤處理已經獲得改進。之前處理錯誤陷阱的方法(你仍然能夠在VFP中繼續使用這些方法)是當錯誤發生時用on error命令來決定要執行的程序,典型的錯誤處理程序是查看error()和message()來肯定發生了什麼錯誤,而後採起相應的動做。
如今VFP提供了一種自動的錯誤處理機制:就是Error方法。若是定義了一個控件或表單中的Error方法,當錯誤發生時它就被自動執行。aerror()是VFP的一個新增函數,經過傳遞一個參數,該函數能夠建立或更新一個含有如下元素的數組
元素
類型 描述
1
數字 錯誤號(與error()相同)
2
字符 錯誤信息(與message()相同)
3
字符 若是有一個錯誤信息參數(與sys(2018)相同),則返回之(例如:一個字段名),無,則返回.NULL.
4
數字或字符 發生錯誤的工做區。若是沒有,則返回.NULL.
5
數字或字符 若是一個觸發器失敗,返回觸發器號(插入爲1,更新爲2,刪除爲3),若是沒有則返回.NULL.
6
數字或字符 .NULL.(應用於OLE和ODBC錯誤)
7
數字 .NULL.(應用於OLE錯誤) 例如:aerror(IaERROR)會建立或更新一個稱爲IaERROR的數組。
如下是VFP在將緩衝寫入表時可能發生的一些錯誤:
錯誤號#
錯誤信息 說明
109
記錄正由其它用戶使用 1539 觸發器失敗 檢測數組的第5個元素能夠肯定是哪一個觸發器失敗了
1581
字段不接受空值(null) 檢測數組的第3個元素能夠肯定是哪一個字段引發的錯誤
1582
違反了字段的驗證規則 檢測數組的第3個元素能夠肯定是哪一個字段引發的錯誤
1583
違反了記錄的驗證規則 1585 記錄已被其它用戶修改 1884 違反了索引的惟一性 檢測數組的第3個元素能夠肯定是哪一個索引標記引發的錯誤 對於以上這些錯誤,大部分能夠直接處理:提示用戶問題所在,而後讓用戶在"編輯"狀態下改正錯誤或是取消操做。對於#1585錯誤(記錄已被其它用戶修改),有如下幾種處理錯誤的方法:
l 能夠提示當前用戶有別人修改了該記錄,而後用tablerevert()取消當前用戶的編輯內容。我想多數人對這種方法會不高興的。
l 能夠用tableupdate(.F. , .T.)來強行更新記錄,使當前用戶的修改覆蓋其它用戶修改。這樣作,當前用戶天然是高興的,但其它用戶可能就要不滿了。:(
l 複製一個相同的表單(在VFP中對同一表單建立多個實例是很是容易的),在上面顯示出其它用戶對該記錄的修改,這樣,當前用戶就能夠決定是保存仍是不保存其它用戶的修改,也就是說,能夠用tableupdate(.F. , .T.)強行更新或是用tablerevert()來取消編輯。
有更好的方案來檢測咱們是否遇到了"真正的"衝突,所謂"真正的"衝突,是指兩個用戶在同一時間修改了同一個字段。若是他們修改的是同一記錄的兩個不一樣字段,咱們能夠只更新當前用戶修改的字段,而保留另外一個用戶修改的字段,使之不受影響。例如,在一個訂單處理系統中,一個用戶修改了產品介紹,而同時另外一個用戶在對該產品下訂單,正在輸入數量,這兩個修改相互獨立,並無衝突,這時咱們不要一次更新整條記錄,而是隻更新本身修改的那個字段,如此一來,兩個用戶都會感到滿意。
如下是實現這一想法的思路:
l 查找oldval()與curval()不一樣的字段,若是有,說明該字段已被其它用戶編輯過,若是該字段的緩衝值與oldval()相同,說明當前用戶並無修改該字段。這種狀況下,咱們能夠將curval()中的值先傳給緩衝值,再更新,這樣就能夠避免緩衝值覆蓋新值了。
l 查找緩衝值與oldval()不一樣的字段,這些字段是當前用戶修改過的字段,若是oldval()又等於curval(),說明其它用戶沒有改動過該字段,這種狀況下,咱們能夠放心地覆蓋掉它。
l 若是咱們找到的字段,其緩衝值與oldval()不一樣,但與curval()相同,這說明兩個用戶對同一字段作了相同的修改。這種狀況看起來好象不大可能,實際上是會發生的。好比有人發來了一個公司的地址變更信息,而偏偏有兩個用戶同時決定據此來更新記錄中的公司地址。由於他們所作的修改內容是相同的,咱們能夠覆蓋另外一個便可。可是,若是是以同一個數量更新一個量值的話(好比兩人同時下訂單,且輸入了相同的數量),你就不能簡單地覆蓋字段,應該將此看做爲"真正的"衝突。
l 若是發現一個字段的緩衝值既不一樣於oldval()也不一樣於curval(),並且oldval()與curval()也各不相同,這說明兩個用戶都修改了同一個字段,且值不相同。這種狀況纔是咱們遇到的真正的衝突,咱們不得不決定怎樣處理這一衝突。
在手工輸入存貨數量或賬目餘額時,有一種可能就是其它用戶輸入的值對緩衝值會產生影響。好比,若是檢測到oldval()是10, curval()是20,說明其它用戶將數額已增長了10;若是如今緩衝值爲5,說明當前用戶要將數額在原基礎(10)上減小5,所以新的緩衝值就應該是value+ oldval()- curval(),即等於15,應該用此數來更新字段。
對於日期型字段的衝突,就要考慮商業規則或按實際狀況處理。例如,在"病人預定時間"程序中,有一個字段保存着病人下次預定醫生的時間,若是該字段兩個日期發生衝突的話,那麼,靠近當前日期的那個日期極可能是正確的。固然,如果其中一個預定日期比當前日期還要早的話(已通過期),那就應該取靠後的那個日期了。
其它類型的字段,特別是字符型和備註型字段,一般狀況下若是不詢問用戶以決定是覆蓋其它用戶的修改仍是取消本身的修改的話,則衝突很差解決。只有當用戶在屏幕上看到其它用戶到底作了什麼修改後,他才能作出正確地判斷。
如下是解決衝突的一些代碼(這些代碼假設已檢測到錯誤碼爲#1585,即記錄已被其它用戶修改過。)
* 檢測每個字段,看哪一個發生了衝突。
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
* 其它用戶編輯了該字段,而當前用戶沒編輯,因此直接用新值便可。
case llOtherUser and not llThisUser
replace (lcField) with curval(lcField)
* 其它用戶沒有編輯該字段,或者兩者作了相同的修改,所以咱們無需作任何處理。
case not llOtherUser or llSameChange
* 兩個用戶以不一樣的值修改了該字段。
otherwise
llConflict = .T.
endcase
next lnI
* 若是發生了衝突,處理之!
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.)
* 經過產生一個表單實例來查看其它用戶的修改內容。
case lnChoice = 7
do form MYFORM name oName
* 取消當前用戶的修改。
otherwise
= tablerevert()
endcase
* 若是沒有發生衝突,則強行更新。
else
= tableupdate(.F., .T.)
endif llConflict
表緩衝的寫入
咱們前面已經講到,能夠用tableupdate(.T.)將表緩衝中的全部記錄一次寫入磁盤。與行緩衝同樣,若是其它用戶修改了表(或是其它什麼出錯緣由)而不能正確更新表,tableupdate(.T.)將返回.F.值。
前文所講的錯誤處理程序在行緩衝模式下運行良好,由於咱們在某一時刻只關心單條記錄。但對於表緩衝來講,咱們不得不考慮每一條記錄,由於在緩衝區中可能既有修改過的記錄,也有未修改過的記錄,咱們怎樣知道到底更新哪條記錄呢?若是用tableupdate(.T.)失敗(返回.F.),狀況就變得更加複雜化:咱們不知道錯在哪條記錄上!並且有些記錄可能被作過"保存",因此還不止一條記錄會發生衝突呢。請不要着急:),VFP新增函數getnextmodified()能夠精確地告訴咱們想知道的信息:該函數返回下一個被修改記錄的記錄號。若是返回值爲0,說明在緩衝區中沒有被修改過的記錄。這個函數接收兩個參數:第一個參數是一個記錄號,正是從這個記錄號開始向下查找下一個被修改的記錄;第二個參數是查找的工做區別名。最被,你應該將0傳給第一個參數,這樣getnextmodified()就會找到第一個被修改的記錄,若要繼續找下一個被修改的記錄,只要將當前記錄的記錄號傳給第一個參數便可。
下面是在剛纔處理衝突的程序基礎上改進後的代碼,它用來處理表緩衝更新失敗時的操做。
* 先找到第一個被修改過的記錄。
lnChanged = getnextmodified(0)
do while lnChanged <> 0
* 移動記錄指針並嘗試鎖定它。
go lnChanged
if rlock()
* 檢測每個字段,看哪一個發生了衝突。
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
* 其它用戶編輯了該字段,而當前用戶沒編輯,因此直接用新值便可。
case llOtherUser and not llThisUser
replace (lcField) with curval(lcField)
* 其它用戶沒有編輯該字段,或者兩者作了相同的修改,所以咱們無需作任何處理。
case not llOtherUser or llSameChange
* 兩個用戶以不一樣的值修改了該字段。
otherwise
llConflict = .T.
endcase
next lnI
* 若是發生了字段衝突,咱們能夠在此處理它,與行緩衝不一樣的是,咱們也能夠如今不處理,由於之後全部記錄將被寫入,到時會處理的。
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
* 若是選擇了覆蓋其它用戶的修改,在此能夠不作處理,由於之後將一次性更新。
case lnChoice = 6
*經過產生一個表單實例來查看其它用戶的修改內容。
&nb
|