http://www.openitpower.com/wenzhang/97/10846_1.html
與TTable、TQuery一樣,TClientDataSet也是從TDataSet繼承下來(lái)的,它通常用于多層體系結(jié)構(gòu)的客戶端。TClientDataSet最大的特點(diǎn)是它不依賴于BDE(Borland Database Engine),但它需要一個(gè)動(dòng)態(tài)鏈接庫(kù)的支持,這個(gè)動(dòng)態(tài)鏈接庫(kù)叫DBCLIENT.DLL。在客戶端,也不需要用TDatabase構(gòu)件,因?yàn)榭蛻舳瞬⒉恢苯舆B接數(shù)據(jù)庫(kù)。
由于TClientDataSet是從TDataSet繼承下來(lái)的,所以,它支持諸如編輯、搜索、瀏覽、糾錯(cuò)、過(guò)濾等功能。由于TClientDataSet在內(nèi)存中建立了數(shù)據(jù)的本地副本,上述操作的執(zhí)行速度很快。也正是由于TClientDataSet并不直接連接數(shù)據(jù)庫(kù),因此,客戶程序必須提供獲取數(shù)據(jù)的機(jī)制。在Delphi 4中,TClientDataSet有三種途徑獲取數(shù)據(jù):
.從文件中存取數(shù)據(jù)。
.從本地的另一個(gè)數(shù)據(jù)集中獲取數(shù)據(jù)。
.通過(guò)IProvider接口從遠(yuǎn)程數(shù)據(jù)庫(kù)服務(wù)器獲取數(shù)據(jù)。
在一個(gè)客戶程序中,可以同時(shí)運(yùn)用上述三種機(jī)制獲取數(shù)據(jù)。
11.1 瀏覽和編輯數(shù)據(jù) 和其他數(shù)據(jù)集構(gòu)件一樣,可以用標(biāo)準(zhǔn)的數(shù)據(jù)控件顯示由TClientDataSet引入的數(shù)據(jù)集,當(dāng)然,這需要借助于TDataSource構(gòu)件。
由于TClientDataSet是從TDataSet繼承下來(lái)的,所以,凡是其他數(shù)據(jù)集構(gòu)件支持的功能,TClientDataSet構(gòu)件也大致具備。不同的是,TClientDataSet能夠在內(nèi)存中建立數(shù)據(jù)的副本,因此,TClientDataSet比其他數(shù)據(jù)集構(gòu)件增加了一些特殊的功能。
11.1.1 瀏覽數(shù)據(jù)
可以用標(biāo)準(zhǔn)的數(shù)據(jù)控件顯示由TClientDataSet引入的數(shù)據(jù)集。在運(yùn)行期,可以調(diào)用諸如First、GotoKey、Last、Next和Prior等函數(shù)來(lái)瀏覽數(shù)據(jù)。
TClientDataSet也支持書(shū)簽功能,可以用書(shū)簽來(lái)標(biāo)記某條記錄,以后就可以方便地找到這條記錄。
對(duì)于TTable、TQuery等數(shù)據(jù)集構(gòu)件來(lái)說(shuō),只能讀RecNo屬性來(lái)判斷當(dāng)前記錄的序號(hào)。對(duì)于TClientDataSet構(gòu)件來(lái)說(shuō),還可以寫(xiě)RecNo屬性,使某一序號(hào)的記錄成為當(dāng)前記錄。
11.1.2 CanModify屬性 TDataSet的CanModify屬性用于判斷數(shù)據(jù)集中的數(shù)據(jù)是否可以修改。CanModify屬性本身是只讀的,也就是說(shuō),數(shù)據(jù)是否能夠修改不取決于應(yīng)用程序。
不過(guò),TClientDataSet構(gòu)件有其特殊性,因?yàn)門ClientDataSet已經(jīng)把數(shù)據(jù)在內(nèi)存中建立了副本,因此,應(yīng)用程序可以決定是否允許修改數(shù)據(jù)。如果不允許用戶修改數(shù)據(jù),只要把ReadOnly屬性設(shè)為True,此時(shí),CanModify屬性肯定返回False。
與其他數(shù)據(jù)集構(gòu)件不同,修改TClientDataSet構(gòu)件的ReadOnly屬性時(shí),不需要事先把Active屬性設(shè)為True。
11.1.3 取消修改 TClientDataSet傳輸數(shù)據(jù)的基本單位稱為數(shù)據(jù)包,當(dāng)前的數(shù)據(jù)包可以由Data屬性來(lái)訪問(wèn)。不過(guò),用戶對(duì)數(shù)據(jù)的修改并不直接反映到Data屬性中,而是臨時(shí)寫(xiě)到一個(gè)日志即Delta屬性中,這樣做的好處是以后隨時(shí)可以取消修改。
不過(guò),這里要說(shuō)明一點(diǎn),盡管用戶的修改并沒(méi)有反映到Data,當(dāng)用戶在數(shù)據(jù)控件中看到的卻是最新修改的數(shù)據(jù)。如果一條記錄被反復(fù)修改了多次,用戶看到的只是最新的數(shù)據(jù),但日志中卻記載了多次。
要取消上一次的修改,調(diào)用UndoLastChange函數(shù)。UndoLastChange需要傳遞一個(gè)布爾類型的參數(shù)叫FollowChange,如果FollowChange參數(shù)設(shè)為True,光標(biāo)就移到被恢復(fù)的記錄上,如果FollowChange參數(shù)設(shè)為False,光標(biāo)仍然在當(dāng)前記錄上。
ChangeCount屬性返回日志中記載的修改次數(shù)。如果一條記錄被反復(fù)修改了多次,每調(diào)用一次UndoLastChange能夠逐級(jí)取消上一次的修改。
UndoLastChange只能取消上一次的修改,如果想一下子取消所有的修改,首先要選擇一個(gè)記錄,然后調(diào)用RevertRecord。RevertRecord將從日志中取消所有對(duì)當(dāng)前記錄的修改。
TClientDataSet還有一個(gè)SavePoint屬性,它能把當(dāng)前的編輯狀態(tài)保存起來(lái),以后隨時(shí)可以返回當(dāng)時(shí)的狀態(tài)。例如,可以這樣保存當(dāng)前的狀態(tài):
BeforeChanges := ClientDataSet1.SavePoint;
以后,可以這樣來(lái)恢復(fù)當(dāng)時(shí)的狀態(tài):
ClientDataSet1.SavePoint := BeforeChanges;
應(yīng)用程序可以保存多處狀態(tài),可以恢復(fù)其中一個(gè)狀態(tài),不過(guò),一旦某個(gè)狀態(tài)被恢復(fù),在其之后的狀態(tài)就無(wú)效。
如果要一下子取消日志中記載的所有修改,可以調(diào)用CancelUpdates函數(shù)。CancelUpdates將把日志清空,取消所有的修改。
如果LogChanges屬性設(shè)為False,用戶對(duì)數(shù)據(jù)的修改就會(huì)直接反映到Data屬性中。
11.1.4 合并修改 要把日志中記載的修改合并到Data屬性中,有兩種方式,具體使用哪一種方式,取決于應(yīng)用程序獲取數(shù)據(jù)的機(jī)制。不過(guò),不管是哪種機(jī)制,合并后,日志自動(dòng)被清空。
對(duì)于一個(gè)從文件中獲取數(shù)據(jù)的程序來(lái)說(shuō),只要調(diào)用MergeChangeLog函數(shù),就把日志中記載的修改合并到Data屬性中。不用擔(dān)心其他用戶同時(shí)修改了數(shù)據(jù)。
對(duì)于一個(gè)從應(yīng)用服務(wù)器獲取數(shù)據(jù)的程序來(lái)說(shuō),就不能調(diào)用MergeChangeLog來(lái)合并數(shù)據(jù),而要調(diào)用ApplyUpdates函數(shù),ApplyUpdates會(huì)把日志中記載的修改傳遞給應(yīng)用服務(wù)器,待應(yīng)用服務(wù)器成功地把數(shù)據(jù)更新了數(shù)據(jù)庫(kù)服務(wù)器后,才會(huì)合并到Data屬性中。
11.1.5 糾錯(cuò) TClientDataSet支持糾錯(cuò)功能。一般情況下,需要自己建立糾錯(cuò)規(guī)則,以便對(duì)用戶輸入的數(shù)據(jù)進(jìn)行糾錯(cuò)。
此外,如果獲得了IProvider接口的話,還可以從遠(yuǎn)程服務(wù)器引入糾錯(cuò)規(guī)則。
有時(shí)候,客戶端可能需要暫時(shí)禁止糾錯(cuò),因?yàn)榭蛻舳藦膽?yīng)用服務(wù)器檢索數(shù)據(jù)是分階段進(jìn)行的,在所有的數(shù)據(jù)檢索完畢之前,有些糾錯(cuò)規(guī)則很可能會(huì)報(bào)錯(cuò)。
要暫時(shí)禁止糾錯(cuò),可以調(diào)用DisableConstraints,要重新允許糾錯(cuò),可以調(diào)用EnableConstraints函數(shù)。DisableConstraints和EnableConstraints實(shí)際上都是作用于一個(gè)內(nèi)部的計(jì)數(shù)。
11.2 索 引 使用索引有這么幾個(gè)好處:
.在數(shù)據(jù)集中定位記錄比較快。
.能夠在兩個(gè)數(shù)據(jù)集之間建立Lookup或Master/Detail關(guān)系。
.可以對(duì)記錄排序。
在多層體系結(jié)構(gòu)中,當(dāng)客戶程序從應(yīng)用服務(wù)器檢索數(shù)據(jù)時(shí),它同時(shí)獲得了默認(rèn)的索引。默認(rèn)的索引叫DEFAULT_ORDER,可以使用這個(gè)索引排序,但不能修改或刪除這個(gè)索引。
除了默認(rèn)的索引外,TClientDataSet還對(duì)日志中記載的記錄自動(dòng)建立了一個(gè)副索引叫CHANGEINDEX。與DEFAULT_ORDER一樣,不能修改或刪除這個(gè)副索引。
另外,還可以使用數(shù)據(jù)集中已建立的其他索引,或者自己建立索引。
11.2.1 創(chuàng)建一個(gè)新的索引 要?jiǎng)?chuàng)建一個(gè)新的索引,可以調(diào)用AddIndex。AddIndex需要傳遞若干個(gè)參數(shù):
一是Name參數(shù),用于指定索引名。在運(yùn)行期切換索引時(shí)需要用到索引的名稱。
二是Fields參數(shù),它是一個(gè)字符串,用于指定索引中的字段名,彼此之間用分號(hào)隔開(kāi)。
三是Options參數(shù),用于設(shè)置索引的選項(xiàng),包含ixDescending元素表示按降序排列,包含ixCaseInsensitive元素表示大小寫(xiě)不敏感。
四是DescFields參數(shù),它也是一個(gè)字符串,用于指定若干個(gè)字段名,這些字段將按照降序排列。
五是CaseInsFields參數(shù),它的作用與DescFields參數(shù)類似,包含在CaseInsFields參數(shù)中的字段將對(duì)大小寫(xiě)不敏感。
六是GroupingLevel參數(shù),用于指定分組級(jí)別,其值不能超過(guò)索引中的字段數(shù)。
下面的代碼創(chuàng)建了一個(gè)索引:
If Edit1.Text <> '' and ClientDataSet1.Fields.FindField(Edit1.Text) then
Begin
ClientDataSet1.AddIndex(Edit1.Text+'Index',Edit1.Text,
[ixCaseInsensitive],'','',0);
ClientDataSet1.IndexName := Edit1.Text + 'Index';
End;
為了避免創(chuàng)建一個(gè)索引,可以臨時(shí)用IndexFieldNames屬性來(lái)指定若干個(gè)字段,讓數(shù)據(jù)集按這些字段排序。
11.2.2 刪除和切換索引 要?jiǎng)h除一個(gè)先前創(chuàng)建的索引,可以調(diào)用DeleteIndex并指定要?jiǎng)h除的索引名稱。注意:DEFAULT_ORDER和CHANGEINDEX不能刪除。
如果建立了多個(gè)索引,可以任意選擇其中的一個(gè)索引,這就要用到IndexName屬性。
11.2.3 用索引把數(shù)據(jù)分組 選擇了一個(gè)索引后,數(shù)據(jù)集將自動(dòng)按其中的字段進(jìn)行排序。這樣,臨近的記錄往往在關(guān)鍵字段上含有相同的值。例如,假設(shè)有一個(gè)表是這樣的:
SalesRep Customer OrderNo Amount
1 1 5 100
1 1 2 50
1 2 3 200
1 2 6 75
2 1 1 10
2 3 4 200
可以看出,SalesRep字段的值有重復(fù)的。對(duì)于SalesRep字段的值為1的來(lái)說(shuō),Customer字段的值也有重復(fù)的。這就是說(shuō),可以按SalesRep字段分組,進(jìn)而再按Customer字段分組。顯然,這里的分組級(jí)別是不同的,按SalesRep字段建立的分組屬于第一級(jí),按Customer字段建立的分組屬于第二級(jí)。實(shí)際上,分組級(jí)別取決于字段在索引中的順序。
TClientDataSet可以決定是否按照分組級(jí)別來(lái)顯示記錄的值。例如,也許想以下面這種形式顯示數(shù)據(jù):
SalesRep Customer OrderNo Amount
1 1 5 100
2 50
2 3 200
6 75
2 1 1 10
2 3 4 200
要判斷當(dāng)前記錄某一級(jí)的什么位置,可以調(diào)用GetGroupState函數(shù)。GetGroupState函數(shù)需要傳遞一個(gè)參數(shù),用于指定分組級(jí)別。
11.3 計(jì) 算 字 段 與其他數(shù)據(jù)集一樣,也可以在TClientDataSet建立的數(shù)據(jù)集中增加計(jì)算字段。計(jì)算字段的值是基于同一個(gè)記錄中的其他字段計(jì)算出來(lái)的。
在其他數(shù)據(jù)集中,只要用戶修改了數(shù)據(jù)或當(dāng)前記錄發(fā)生改變,就會(huì)觸發(fā)OnCalcFields事件,換句話說(shuō),計(jì)算字段的值就被計(jì)算一次。
TClientDataSet引入了“內(nèi)部計(jì)算字段”的概念。與一般的計(jì)算字段不同的是,內(nèi)部計(jì)算字段的值將隨其他字段的值一起存取,這樣,只有當(dāng)用戶修改了數(shù)據(jù)才會(huì)觸發(fā)OnCalcFields事件,如果僅僅改變了當(dāng)前記錄,不會(huì)觸發(fā)OnCalcFields事件。也就是說(shuō),內(nèi)部計(jì)算字段的值需要重新計(jì)算的機(jī)會(huì)大大減少。
在處理OnCalcFields事件的句柄中,首先要判斷State屬性。如果State屬性返回dsInternalCalc,此時(shí)需要計(jì)算內(nèi)部計(jì)算字段的值。如果State屬性返回dsCalcFields,此時(shí)需要計(jì)算一般的計(jì)算字段的值。
11.4 統(tǒng) 計(jì) 值 TClientDataSet增加了統(tǒng)計(jì)的功能,它可以基于分組自動(dòng)計(jì)算總和、平均、計(jì)數(shù)、最大、最小值。當(dāng)用戶編輯數(shù)據(jù)時(shí),這些統(tǒng)計(jì)值會(huì)自動(dòng)跟著變化。
11.4.1 指定統(tǒng)計(jì)方式 要指定怎樣進(jìn)行統(tǒng)計(jì),就要用到Aggregates屬性。這個(gè)屬性是一個(gè)TAggregates對(duì)象,它用于管理一組TAggregate對(duì)象。
在設(shè)計(jì)期,可以單擊Aggregates屬性邊上的省略號(hào)按鈕打開(kāi)如圖11.1所示
的編輯器。
圖11.1 管理一組TAggregate對(duì)象
單擊按鈕可以增加一個(gè)TAggregate對(duì)象,單擊按鈕可以刪減一個(gè)TAggregate對(duì)象,單擊按鈕可以把TAggregate對(duì)象前移,單擊按鈕可以把TAggregate對(duì)象后移。
可以用字段編輯器專門創(chuàng)建一個(gè)用于表達(dá)統(tǒng)計(jì)值的字段,該字段的類型必須是“Aggregate”。Delphi 4會(huì)自動(dòng)創(chuàng)建一個(gè)TAggregate對(duì)象,并加到Aggregates屬性中。選擇一個(gè)TAggregate對(duì)象,Object Inpector將顯示該對(duì)象的屬性。
其中,Expression屬性用于指定統(tǒng)計(jì)表達(dá)式,例如:
Sum(Field1)
也可以是比較復(fù)雜的表達(dá)式:
Sum(Qty * Price) - Sum(AmountPaid)
在表達(dá)式中,可以使用下列統(tǒng)計(jì)運(yùn)算符:
.Sum計(jì)算一組數(shù)據(jù)的總和。
.Avg計(jì)算一組數(shù)據(jù)的平均值。
.Count計(jì)算一組數(shù)據(jù)中的非空值的個(gè)數(shù)。
.Min計(jì)算一組數(shù)據(jù)的最小值。
.Max計(jì)算一組數(shù)據(jù)的最大值。
除了上述幾個(gè)統(tǒng)計(jì)運(yùn)算符外,還可以使用過(guò)濾條件中所能使用的運(yùn)算符,但不能嵌套。在一個(gè)表達(dá)式中,可以混合出現(xiàn)幾個(gè)統(tǒng)計(jì)值或常量,但不能混合出現(xiàn)統(tǒng)計(jì)值和字段。
Sum(Qty * Price){合法}
Max(Field1) - Max(Field2){合法}
Avg(DiscountRate) * 100{合法}
Min(Sum(Field1)){非法,不能嵌套}
Count(Field1) - Field2{非法,統(tǒng)計(jì)值和字段不能混合出現(xiàn)在一個(gè)表達(dá)式中}
11.4.2 指定分組 默認(rèn)情況下,統(tǒng)計(jì)值是基于數(shù)據(jù)集中所有的記錄計(jì)算出來(lái)的。不過(guò),也可以針對(duì)一部分記錄計(jì)算統(tǒng)計(jì)值,這就需要事先建立分組。
前面在介紹索引時(shí)已經(jīng)提到分組的概念。可以通過(guò)IndexName屬性和GroupingLevel屬性來(lái)選擇使用哪個(gè)索引以及最大的分組級(jí)別。
例如,假設(shè)有一個(gè)表是這樣的:
SalesRep Customer OrderNo Amount
1 1 5 100
1 1 2 50
1 2 3 200
1 2 6 75
2 1 1 10
2 3 4 200
如果要按SalesRep字段分組,并且指定其中的第一級(jí),程序代碼應(yīng)當(dāng)這樣寫(xiě):
Agg.Expression := 'Sum(Amount)';
Agg.IndexName := 'SalesCust';
Agg.GroupingLevel := 1;
Agg.AggregateName := 'Total for Rep';
11.4.3 怎樣獲取統(tǒng)計(jì)值 要獲取統(tǒng)計(jì)值,可以調(diào)用TAggregate對(duì)象的Value函數(shù)。如果統(tǒng)計(jì)值是基于數(shù)據(jù)集中所有的記錄計(jì)算出來(lái)的,隨時(shí)可以調(diào)用Value函數(shù)。如果統(tǒng)計(jì)值是基于分組計(jì)算出來(lái)的,必須保證當(dāng)前記錄正好位于該分組內(nèi)。因此,在調(diào)用Value之前,最好先調(diào)用GetGroupState函數(shù)看看當(dāng)前記錄是否位于該分組內(nèi)。
要在數(shù)據(jù)控件中顯示統(tǒng)計(jì)值,必須事先在字段編輯器中創(chuàng)建一個(gè)永久字段對(duì)象,該字段的類型必須是Aggregate。
11.5 數(shù) 據(jù) 包 通過(guò)Data屬性可以訪問(wèn)客戶程序從應(yīng)用服務(wù)器檢索到的數(shù)據(jù)。程序示例如下:
Procedure TForm1.Button1Click(Sender: TObject);
Begin
ClientDataSet1.Data := ClientDataSet1.Provider.DataRequest(FilterEdit.Text);
End;
11.5.1 直接對(duì)Data屬性賦值 前面講過(guò),客戶程序既可以通過(guò)IProvider接口獲取數(shù)據(jù),也可以從另一個(gè)數(shù)據(jù)集獲取數(shù)據(jù),后者就是通過(guò)Data屬性賦值的。程序示例如下:
ClientDataSet1.Data := ClientDataSet2.Data;
一旦Data被賦值,就可以用標(biāo)準(zhǔn)的數(shù)據(jù)控件顯示這些數(shù)據(jù)。
注意:當(dāng)從另一個(gè)數(shù)據(jù)集獲取數(shù)據(jù)時(shí),另一個(gè)數(shù)據(jù)集的日志也將被復(fù)制過(guò)來(lái),但不包括原來(lái)的范圍和過(guò)濾條件。
如果要從另一個(gè)基于BDE的數(shù)據(jù)集中獲取數(shù)據(jù),可以通過(guò)數(shù)據(jù)集構(gòu)件的Provider屬性,程序示例如下:
ClientDataSet1.Data := Table1.Provider.Data;
如果要從一個(gè)自定義的數(shù)據(jù)集獲取數(shù)據(jù),首先要?jiǎng)?chuàng)建一個(gè)臨時(shí)的TProvider構(gòu)件,然后設(shè)置其DataSet屬性指定這個(gè)自定義的數(shù)據(jù)集。程序示例如下:
TempProvider := TDataSetProvider.Create(Form1);
TempProvider.DataSet := SourceDataSet;
ClientDataSet1.Data := TempProvider.Data;
TempProvider.Free;
11.5.2 在數(shù)據(jù)包中加入自定義的信息 可以把自定義的信息加到數(shù)據(jù)包中。當(dāng)把數(shù)據(jù)保存到文件或流中時(shí),這些自定義的信息也將保存到文件或流中。如果把數(shù)據(jù)包直接賦值給另一個(gè)數(shù)據(jù)集的話,這些自定義的信息也將被復(fù)制。
要把自定義的信息加到數(shù)據(jù)包中,可以調(diào)用SetOptionalParam函數(shù)。要從數(shù)據(jù)包中檢索自定義的信息,可以調(diào)用GetOptionalParam。程序示例如下:
Procedure TAppServer.Provider1UpdateData(Sender: TObject; DataSet: TClientDataSet);
var
WhenProvided: TDateTime;
Begin
WhenProvided := DataSet.GetOptionalParam('TimeProvided');
...
End;
11.5.3 克隆另一個(gè)數(shù)據(jù)集 調(diào)用TClientDataSet的CloneCursor函數(shù)可以獲得一個(gè)數(shù)據(jù)集的完全相同的副本。它與直接通過(guò)Data屬性賦值是有區(qū)別的。
區(qū)別之一:數(shù)據(jù)在兩個(gè)數(shù)據(jù)集之間是共享的,修改其中一個(gè)將同時(shí)修改另一個(gè)。
區(qū)別之二:除了數(shù)據(jù)外,CloneCursor函數(shù)還復(fù)制了一些屬性和事件,這取決于Reset和KeepSettings參數(shù)怎樣設(shè)置。
CloneCursor函數(shù)需要傳遞三個(gè)參數(shù),其中,Source參數(shù)指定源數(shù)據(jù)集,Reset參數(shù)和KeepSettings參數(shù)用于設(shè)置除了數(shù)據(jù)外是否還要復(fù)制下列屬性和事件:Filter、Filtered、FilterOptions、OnFilterRecord、IndexName、MasterSource、MasterFields、ReadOnly、RemoteServer、ProviderName、Provider。
如果Reset和KeepSettings參數(shù)都設(shè)為False,源數(shù)據(jù)集的上述屬性和事件都將被復(fù)制給目標(biāo)數(shù)據(jù)集。如果Reset參數(shù)設(shè)為True,目標(biāo)數(shù)據(jù)集的上述屬性和事件都將被清空。如果Reset參數(shù)設(shè)為False,而KeepSettings參數(shù)設(shè)為True,目標(biāo)數(shù)據(jù)集的上述屬性和事件不變,不過(guò),必須保證這些屬性和事件與克隆后的數(shù)據(jù)相容。
11.6 與應(yīng)用服務(wù)器通訊 在多層體系結(jié)構(gòu)中,客戶程序通過(guò)IProvider接口與應(yīng)用服務(wù)器交換數(shù)據(jù)。這一章介紹怎樣在客戶端獲得IProvider接口、怎樣向應(yīng)用服務(wù)器傳遞參數(shù)、怎樣向應(yīng)用服務(wù)器請(qǐng)求數(shù)據(jù)、怎樣把用戶對(duì)數(shù)據(jù)的修改寫(xiě)到數(shù)據(jù)庫(kù)中。
11.6.1 怎樣在客戶端獲得IProvider接口 在單層應(yīng)用程序以及工作在“公文包”模式下的多層應(yīng)用程序中,不需要用到IProvider接口。而在多層體系結(jié)構(gòu)中,客戶程序要與應(yīng)用服務(wù)器交換數(shù)據(jù),首先必須獲得IProvider接口,這就要用到RemoteServer屬性和ProviderName屬性。
RemoteServer屬性用于指定客戶端的MIDAS連接構(gòu)件。MIDAS連接構(gòu)件又稱Data Broker,用于建立和維護(hù)與應(yīng)用服務(wù)器的連接。
在設(shè)計(jì)期,正確設(shè)置了RemoteServer屬性后,就可以在對(duì)象觀察器中為ProviderName屬性選擇一個(gè)值,實(shí)際上就是選擇應(yīng)用服務(wù)器上的一個(gè)TProvider構(gòu)件。
11.6.2 向應(yīng)用服務(wù)器傳遞參數(shù) 客戶程序可以向應(yīng)用服務(wù)器傳遞參數(shù),這些參數(shù)實(shí)際上是傳遞給應(yīng)用服務(wù)器上的TQuery構(gòu)件或TStoredProc構(gòu)件。既可以在設(shè)計(jì)期也可以在運(yùn)行期設(shè)置參數(shù)。
在設(shè)計(jì)期,可以單擊Params屬性邊上的省略號(hào)按鈕,打開(kāi)一個(gè)如圖11.2所示的編輯器。
圖11.2 設(shè)置參數(shù)
單擊按鈕可以增加一個(gè)參數(shù),單擊按鈕可以刪減一個(gè)參數(shù),單擊按鈕可以把一個(gè)參數(shù)前移,單擊按鈕可以把一個(gè)參數(shù)后移。
選擇一個(gè)參數(shù),對(duì)象觀察器將顯示該參數(shù)(TParam對(duì)象)的屬性。
在運(yùn)行期可以調(diào)用TParams的CreateParam函數(shù)來(lái)創(chuàng)建一個(gè)參數(shù)。例如,下面的代碼創(chuàng)建了一個(gè)參數(shù)叫CustNo,它的使用類型是ptInput,數(shù)據(jù)類型是ftInteger,它的值設(shè)為605。
With ClientDataSet1.Params.CreateParam(ftInteger, 'CustNo', ptInput) Do
AsInteger := 605;
設(shè)置好參數(shù)以后,如果TClientDataset的Active屬性是False,只要把Active屬性設(shè)為True,這些參數(shù)將被自動(dòng)傳遞給應(yīng)用服務(wù)器。如果Active屬性已經(jīng)為True,就要調(diào)用SendParams函數(shù)把參數(shù)傳遞給應(yīng)用服務(wù)器。
注意:傳遞給應(yīng)用服務(wù)器的參數(shù)必須與TQuery構(gòu)件或TStoredProc構(gòu)件的參數(shù)匹配,包括名稱、數(shù)據(jù)類型和參數(shù)類型。
11.6.3 怎樣向應(yīng)用服務(wù)器請(qǐng)求數(shù)據(jù) TClientDataSet提供了兩個(gè)屬性和三個(gè)方法,用于怎樣向應(yīng)用服務(wù)器請(qǐng)求數(shù)據(jù):
一是FetchOnDemand屬性。如果這個(gè)屬性設(shè)為True,TClientDataSet會(huì)根據(jù)需要自動(dòng)檢索附加的數(shù)據(jù)包,例如BLOB字段的值或者嵌套表的內(nèi)容。如果這個(gè)屬性設(shè)為False,程序需要顯式地調(diào)用GetNextPacket才能獲得這些附加的數(shù)據(jù)包。
二是PacketRecords屬性,用于設(shè)置一個(gè)數(shù)據(jù)包中最多可容納的記錄數(shù),設(shè)為-1表示一個(gè)數(shù)據(jù)包可以容納數(shù)據(jù)集的所有記錄。
三是GetNextPacket函數(shù),用于向應(yīng)用服務(wù)器檢索下一個(gè)數(shù)據(jù)包,并把檢索到的數(shù)據(jù)包添加到前一次檢索到的數(shù)據(jù)包的后面。這個(gè)函數(shù)返回實(shí)際檢索到的記錄數(shù)。
四是FetchBlobs過(guò)程,用于從應(yīng)用服務(wù)器檢索BLOB字段的值。如果FetchOnDemand屬性設(shè)為True,就沒(méi)必要調(diào)用FetchBlobs函數(shù)。
五是FetchDetails過(guò)程,用于檢索嵌套表中的數(shù)據(jù)。如果FetchOnDemand屬性設(shè)為True,就沒(méi)必要調(diào)用FetchDetails函數(shù)。
11.6.4 更新數(shù)據(jù)庫(kù) 在多層體系結(jié)構(gòu)中,用戶在客戶端修改了數(shù)據(jù)后,需要把最新的數(shù)據(jù)寫(xiě)到數(shù)據(jù)庫(kù)中,這就要調(diào)用TClientDataSet的ApplyUpdates函數(shù)。
ApplyUpdates只需要傳遞一個(gè)參數(shù)叫MaxErrors,用于指定一個(gè)整數(shù),當(dāng)遇到無(wú)法更新的記錄超過(guò)這個(gè)數(shù)時(shí),此次更新就中止。如果MaxErrors參數(shù)設(shè)為0,表示只要遇到一個(gè)錯(cuò)誤更新就中止,客戶端的日志保持不變。如果MaxErrors參數(shù)設(shè)為-1,當(dāng)應(yīng)用服務(wù)器發(fā)現(xiàn)有錯(cuò)誤的記錄,就嘗試更新下一個(gè)記錄,等所有的記錄都嘗試過(guò)以后才返回。
ApplyUpdates會(huì)自動(dòng)調(diào)用Reconcile函數(shù),進(jìn)而調(diào)用應(yīng)用服務(wù)器上的TProvider構(gòu)件的ApplyUpdates函數(shù)去更新遠(yuǎn)程的數(shù)據(jù)庫(kù)服務(wù)器。沒(méi)有被DBMS服務(wù)器認(rèn)可的記錄通過(guò)Reconcile返回給客戶端,此時(shí)將在客戶端觸發(fā)OnReconcileError事件讓您更正錯(cuò)誤。最后,ApplyUpdates函數(shù)返回仍然沒(méi)有被認(rèn)可的記錄數(shù)。
11.7 在文件中存取數(shù)據(jù) 要從文件中讀取數(shù)據(jù),可以調(diào)用LoadFromFile函數(shù)。LoadFromFile函數(shù)需要傳遞一個(gè)參數(shù),用于指定文件名。文件名應(yīng)包含完整的路徑。如果客戶程序總是從一個(gè)固定的文件中讀取數(shù)據(jù),可以設(shè)置FileName屬性指定一個(gè)文件名,以后,當(dāng)TClientDataSet引入的數(shù)據(jù)集打開(kāi)時(shí),就自動(dòng)從這個(gè)文件中讀取數(shù)據(jù),不需要調(diào)用LoadFromFile。
要從流中讀取數(shù)據(jù),可以調(diào)用LoadFromStream。LoadFromStream需要傳遞一個(gè)參數(shù),用于指定一個(gè)流對(duì)象。
注意:LoadFromFile(LoadFromStream)只能從先前用SaveToFile(SaveToStream)保存的文件中讀取數(shù)據(jù)。
要把數(shù)據(jù)保存到文件中,可以調(diào)用SaveToFile函數(shù)。SaveToFile需要傳遞一個(gè)參數(shù),用于指定文件名。如果指定的文件已存在,文件中的數(shù)據(jù)將被覆蓋。如果客戶程序總是把數(shù)據(jù)保存到一個(gè)固定的文件中,可以設(shè)置FileName屬性指定一個(gè)文件名,當(dāng)TClientDataSet引入的數(shù)據(jù)集關(guān)閉時(shí),就自動(dòng)把數(shù)據(jù)保存到這個(gè)文件中,不需要調(diào)用SaveToFile。
要把數(shù)據(jù)保存到流中,可以調(diào)用SaveToStream。SaveToStream需要傳遞一個(gè)參數(shù),指定一個(gè)流對(duì)象。
注意:當(dāng)把數(shù)據(jù)保存到文件或流中時(shí),日志中記載的修改仍然保留。這樣,當(dāng)下次調(diào)用LoadFromFile或LoadFromStream讀取數(shù)據(jù)時(shí),仍然可以恢復(fù)原來(lái)的數(shù)據(jù)。