3atv精品不卡视频,97人人超碰国产精品最新,中文字幕av一区二区三区人妻少妇,久久久精品波多野结衣,日韩一区二区三区精品

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Delphi:ClientDataset+TDataSetProvider的数据保存问题

發布時間:2025/5/22 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Delphi:ClientDataset+TDataSetProvider的数据保存问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

看到一篇介紹ClientDataSet和TDataSetProvider,非常精彩,特此保存。

===========================================================================

TClientDataSet用法


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


--------------------------------------------------------------------------------
?
-- ?作者:gzkhrh
-- ?發布時間:2005-7-29 8:42:46


-- ?


我們也跟著學學


==============


Delphi做為一個快速應用開發工具,深受程序員的喜愛。其強大的組件功能,讓程序員能夠輕松、高效地完成常見的界面開發、數據庫應用等功能。然而,幫助的相對缺乏,使得許多組件的功能并不為人們正確地使用,究其原因,仍然是認識上的問題。對于MIDAS開發中的核心部件,TClientDataSet和TDataSetProvider,由于資料的缺乏,人們在網上大多談論的是李維的書籍內容。我有幸在BDN上見到了Cary Jensen的Professional Developer系列文章,詳細闡述了DELPHI的數據庫開發技術。現節選出其中的ClientDataSet部分,與大家共同分享。?


ClientDataSet是一個功能強大的類,通過在內存中模擬表格,實現了其它數據集組件所不具備的強大功能。以往只在Delphi和C++ Builder企業版中才提供這個組件,如今,Borland的全部產品(包括最新的Kylix)都集成了TClientDataSet組件。?


TClientDataSet從類的繼承關系上來看,是TDataSet這個抽象類的子類,所以我們可以在TDataSet這個抽象層次上對其進行我們熟悉的操作,比如導航、排序、過濾、編輯。要注意的是,TClientDataSet使用了一種全新的技術,它將所有的數據均放在內存中,所以TClientDataSet是個只存在內存中的“虛擬表”,因此對數據庫的操作是非常快的。在PIII 850,512MB的機器上對十萬條記錄進行建索引的操作,花費的時間少于半分鐘。?


與一般的數據集組件不同,TClientDataSet使用的技術比較特別,本著高速度、低存儲需求的原則,TClientDataSet的內部使用了兩個數據存儲源。第一個是其Data屬性,這是當前內存數據的視圖,反映了所有的數據改變。如果用戶從數據中刪除一條記錄,則此記錄將從Data中消失,相應地,加入一條新記錄后,此記錄便存在Data屬性中了。?


另一個數據源是Delta屬性,故名思義,即增量的意思,這個屬性反映了對數據的改變。無論是向Data屬性新增還是刪除記錄,都會在Delta中記錄下來,如果是修改了Data中的記錄,則會在Delta保存兩條相應的記錄,一條是原始記錄,另一條僅包含修改的字段值。正因為Delta的存在和TClientDataSet在內存中記錄數據的特點,所有的改變都沒有立即更新加對應的物理存儲中,可以根據這些信息在適當的時候恢復,所以TClientDataSet天生具有緩沖更新功能。?


為了使數據更新回數據存儲源,我們要調用TClientDataSet中對應的方法。如果ClientDataSet與DataSetProvider關聯,那么僅需調用TClientDataSet的ApplyUpdates方法即可保存數據的更新,但如果TClientDataSet沒有對應的TDataSetProvider存在,而是直接同文件關聯,那么,這種方式是非常有趣的,我們在BriefCase模型中會再次講解這個問題。此時,如果使用TClientDataSet的SaveToFile和LoadFromFile,都會保留著Delta。調用MergeChangeLog和ClearChanges后,Delta的內容才會被?
清空。只是前者是將Delta的數據同Data結合起來,將改變存儲到物理介質上,而ClearChanges則是一股腦兒全部清空,將數據回復到原始狀態。大部分的應用都是將TClientDataSet與TDataSetProvider結合使用的。兩者聯合使用的行為反映了Borland的設計宗旨,就是要提供一個面向分布式環境的思路。我們下面來慢慢解釋。?


當我們將TClientDataSet對象的Active屬性設為True或者調用其Open方法后,ClientDataSet會向DataSetProvider發送一個取數據包請求。于是DataSetProvider便會打開對應的數據集,將記錄指針指向第一條記錄,然后從頭到尾依次掃描。對于掃描到的每一條記錄,都會將其編碼成一個variant數組,我們通常將它稱之為數據包。完成掃描后,DataSetProvider會關閉指向的數據集,并將所有的這些數據包傳遞給ClientDataSet。在我提供的演示程序中,你可以清楚地看到這種行為(畢竟眼見為實嗎!)。程序主界面右邊的DBGrid連接到一個指向數據庫表的數據源,DataSetProvider即指向此表。當選擇了ClientDataSet | Load菜單項時,你可以看到表格的數據被依次掃描,一旦到達最后一條記錄,表格便會被關閉,右邊的DBGrid被清空,而左邊反映ClientDataSet數據的DBGrid便出顯示出內存中的數據來。由于這個過程會在DBGrid上反映出來,所以不到1000條記錄的取出時間中,大部分都浪費在屏幕的更新顯示上了,你可以選擇ClientDataSet | View Table Loading來禁止顯示,而達到加速的目的。?


在上面的描述中,我們沒有提到一個重要的環節,即數據包是如何還原成表格的。那是因為DataSetProvider會將數據包中的元數據解碼出來,根據元數據(我們可以理解為數據表的結構)便可以構造出與物理數據表一模一樣的內存虛擬表。但要注意的是,盡管DataSetProvider指向的數據表可能有多個索引,但這些信息是不會放在數據包中的,換句話說,ClientDataSet當中的數據默認情況下是無索引的。但因為ClientDataSet具有與TDataSet一致的行為,所以我們可以在此基礎上根據需要重建索引。?


在ClientDataSet中的數據被修改后,可以提交給物理數據表持久化這此改變。這個工作便是由DataSetProvider完成的。內部工作原理是:DataSetProvider創建一個TSQLResolver的實例,這個實例會生成要在底層數據上執行更改的SQL語句。詳細地說,就是對修改日志中的每一條被刪除、插入、更改記錄生成對應的SQL語句。這個語句的生成也可以由用戶控制,DataSetProvider的UpdateMode屬性和ClientDataSet中的ProviderFlags屬性都對SQL語句的生成有影響。?


當然,你也可以換一種方式,即采取同單機或C/S結構一樣的數據直接操作機制,繞過SQL語句和緩沖更新機制來修改數據庫。只需將ResolveToDataSet屬性設為True,那么DataSetProvider在持久化更新時便不會使用TSQLResolve,而是直接修改物理數據源。即定位到要刪除的記錄,調用刪除語句,定位到修改記錄,調用修改語句。我們可以對演示程序稍加修改,觀察此種行為。請將演示程序中的DataSetProvider的ResolveToDataSet屬性由False改為True,運行。在界面中修改數據并且保存,你將會看到右邊的導航按鈕會在瞬間變得可用。?


更絕妙的是,Borland考慮到了應用的多樣性,為我們提供了BeforeUpdateRecord事件,這樣,當DataSetProvider對每個修改日志的記錄進行操作時,都會觸發此事件,我們可以在此事件中加入自己的處理,如“加密操作”、“商業敏感數據處理”等應用,從而極大地方便了程序員,讓程序員對于數據具有完全的控制能力。分布式環境的復雜性對數據的存取提出了更高的要求,所以使用事務來保證數據的完整性和一致性是非常必要的,Borland考慮到了這一點,當調用ClientDataSet的ApplyUpdates時,你可以傳遞一個整數值來指明可以容忍的錯誤數量。如果你的數據非常嚴格,則可以傳遞0值,這樣,DataSetProvider在應用修改時便會打開一個事務,如果遇到錯誤,便會回退此事務,修改日志將保持原樣,并且將出錯的記錄標記出來,最后會觸發OnReconcileError事件。如果傳遞了一個大于0的數,則當出現的錯誤數量小于此指定值時,事務會被提交,發生錯誤而導致提交失敗的記錄會保留在Delta中,而提交成功的記錄會從修改日志中刪除。若錯誤數量達到指定值,則事務會回退,結果同整數值為0的情況。如果值為負數,則會交所以可提交的數據都提交,不可提交的數據仍然保存在修改日志中,并將出錯記錄標記出來。?


雖然,Borland是為了滿足分布式編程的需要而設計了TClientDataSet,但在其它類型的編程環境中使用ClientDataSet也具有積極的意義。首先,我們可以看到,由于數據均在內存中進行操作,而且僅在打開數據庫取數據時和將修改持久到回數據庫時,才有數據庫開銷,其它時間數據庫為零,這樣就極大地增加了數據庫的負荷,讓數據庫服務器能滿足更多用戶的連接請求。其次,ClientDataSet具有其它數據集所不具備的許多高級功能,這為程序員進行復雜的編程提供了便利,可以不考慮數據庫本身是否支持這此功能,而讓ClientDataSet去處理這些復雜而繁瑣的細節。最后,ClientDataSet在數據存儲和應用程序間起到一個抽象層的作用。假如你的程序使用了TClientDataSet,那么如果你以后要更改數據庫存儲機制。比如說由BDE移植到dbExpress,或者從ADO移植到Interbase Express,你的用戶界面和數據控制部分幾乎就不用改變,只需要將DataSetProvider指向新的數據存取組件即可。順便說一句,由于緩沖更新的存在,用戶可能非常厭惡調用ApplyUpdates操作,那么你可以將此調用放入AfterPost和AfterDelte中,讓用戶的操作更方便。


第三章 創建多層應用程序
  一個多層的Client/Server應用程序在邏輯上劃分為幾個部分,分別在不同的機器上運行,這些機器既可以在一個局域網內,也可以在Internet上。多層體系結構最大的優勢可以概括為兩點,一是集中化的商業邏輯,另一個是客戶程序可以做得很“瘦”。
  目前較常見的是三層的體系結構,其中,最關鍵的是應用服務器,它在三層體系結構中起了承上啟下的作用,所以,應用服務器又叫Data Broker。Delphi4可以創建應用服務器,也可以創建“瘦”客戶。如果不怕麻煩的話,也可以創建數據庫后端。
  在更復雜的多層體系結構中,“瘦”客戶與遠程服務器之間可以加入更多的服務中間件,例如,可以加入一個安全服務中間件,或者加入一個轉換中間件,專門用來處理不同平臺共享數據的問題。一旦您真正理解了三層的體系結構,多層的體系結構就迎刃而解。
3.1 多層體系結構的概述
  Delphi 4對多層體系結構的支持主要得益于它的MIDAS技術。MIDAS是Multi-tier Distributed Application Services Suite的簡稱。MIDAS技術與Delphi 4中的另一個關鍵技術DAX配合起來使用,可以使多層的體系結構分布在Intrenet/Intranet上。
3.1.1 多層體系結構的優勢
  在多層體系結構中,由于服務器集中實現了應用邏輯(又稱商業規則),客戶程序可以把重點放在顯示數據和與用戶交互上,客戶程序甚至都不需要知道數據存儲在哪兒。
  具體來說,多層的體系結構具有如下優勢:
  在一個共享的中間層封裝了商業規則。不同的客戶程序可以共享同一個中間層,而不必由每個客戶程序單獨實現商業規則。
  客戶程序可以做得很“瘦”。因為很多復雜的工作由應用服務器代勞了,客戶程序只需要關注用戶界面本身。“瘦”客戶程序更容易發布、安裝、配置和維護。
  實現了分布式數據處理。把一個應用程序分布在幾個機器上運行,可以提高應用程序的性能,通過冗余配置還可以保證不會因為局部故障導致整個應用程序崩潰。
  有利于安全。可以把一些敏感的功能放在有嚴密防護措施的層上,同時又不至于使用戶界面變得復雜。Delphi 4中的CORBA或MTS支持較復雜的安全機制。
3.1.2 MIDAS技術
  MIDAS技術是多層體系結構的關鍵。無論是應用服務器端還是客戶端,MIDAS技術需要有DBCLIENT.DLL的支持,這個動態鏈接庫用于管理數據包。發布MIDAS應用程序時需要購買服務器許可。
  基于MIDAS的多層應用程序需要用到一些特殊的構件,這些構件分為四大種類:對象庫中的遠程數據模塊。遠程數據模塊與普通的數據模塊有些相似,不同的是,遠程數據模塊可以作為COM服務器或CORBA服務器讓客戶程序訪問它的接口。
  TDataSetProvider和TProvider構件。這兩個構件用在應用服務器端,主要作用是提供IProvider接口,客戶程序通過IProvider接口獲得數據和更新數據集。
  TClientDataSet構件。這是一個從TDataset繼承下來的但不需要BDE的構件。MIDAS連接構件。包括TDCOMConnection、TSocketConnection、TCorbaConnection TOLEnterpriseConnection、TMIDASConnection和TRemoteServer。其中,TMIDASConnection和TRemoteServer是為了兼容Delphi3的代碼而保留的。MIDAS連接構件的作用是為客戶程序定位服務器和IProvider接口。每個MIDAS連接構件都以一種特定的通訊協議工作。
3.1.3 MIDAS應用程序是怎樣工作的
  用戶首先要啟動客戶程序,客戶程序將試圖連接應用服務器,如果應用服務器還沒有運行,客戶程序將激活應用服務器,并從中獲得IProvider接口。
  客戶程序向應用服務器請求數據。如果TClientDataSet的FetchOnDemand屬性設為True,客戶程序會根據需要自動檢索附加的數據包如BLOB字段的值或嵌套表的內容。否則,客戶程序需要顯式地調用GetNextPacket才能獲得這些附加的數據包。
  應用服務器收到客戶程序的請求后,就從遠程數據庫服務器那兒檢索數據,并打包返回給客戶程序
  客戶程序收到數據包后把包打開,然后顯示或進行處理。
  用戶對數據進行編輯修改,然后向應用服務器申請更新數據,實際上也要打包。
  應用服務器收到客戶程序的申請后,就向遠程數據庫服務器申請更新數據。如果出錯,應用服務器就把出錯的記錄返回給客戶程序去核對。
  客戶程序核對并修改了數據后,既可以放棄此次更新,也可以繼續此次更新。
3.1.4 客戶程序的結構
  對于最終用戶來說,多層體系結構中的客戶程序與兩層體系結構中的應用程序沒有什么區別,在結構上,客戶程序就好像一個基于文件的單層應用程序一樣,仍然通過標準的數據控件與用戶交互。但與單層應用程序不同的是,多層體系結構中的客戶程序是通過應用服務器提供的IProvider接口獲得數據的,也通過IProvider接口申請更新數據。
  注意:當使用MTS的時候,可以選擇不使用IProvider接口。不使用IProvider接口的好處是,可以充分發揮MTS在處理事務方面的特長。
  在客戶程序中,MIDAS連接構件扮演著極其重要的角色。不同的MIDAS連接構件使用不同的通訊協議:
. TDCOMConnection DCOM
. TSocketConnection Windows Sockets (TCP/IP)l
. TOLEnterpriseConnection OLEnterprise (RPCs)
. TCorbaConnection CORBA (IIOP)
  TRemoteServer和TMIDASConnection是為了兼容Delphi 3的代碼而保留的。
3.1.5 應用服務器的結構
  應用服務器的關鍵部件是遠程數據模塊,它提供了IDataBroker接口。當客戶程序與應用服務器建立了連接,就通過IDataBroker接口來獲得IProvider接口。
  Delphi 4支持三種類型的遠程數據模塊:
  TremoteDataModule。這是一個支持雙重接口的自動化服務器,這種類型的遠程數據模塊適合于使用DCOM、TCP/IP或OLEnterprise方式。
  TMTSDataModule。這也是一個支持雙重接口的自動化服務器,用這種類型的遠程數據模塊創建的應用服務器是Active Library即動態鏈接庫,適合于使用DCOM、TCP/IP或OLEnterprise方式。
  TcorbaDataModule。這是CORBA服務器,適用于與CORBA客戶通訊。
  上述三種遠程數據模塊都可以作為容器,但只能放置非可視的構件。另外,遠程數據模塊上一般要放一個或幾個TDataSetProvider或TProvider構件來提供IProvider接口 。
  遠程數據模塊上也可以放TDatabase構件和TSession構件。
3.1.6 MTS
  MTS是Microsoft Transaction Server的簡稱,是Microsoft為分布式環境下進行事務處理所設計的服務接口。使用TMTSDataModule類型的遠程數據模塊的優勢是:
  MTS為應用服務器提供了基于角色的安全機制。每個客戶都扮演著一種角色,決定了他們能否訪問遠程數據模塊的接口。TMTSDataModule有一個函數叫IsCallerInRole,可以用來檢查客戶的角色,然后有條件地開放該角色所允許的功能。
  MTS提供了緩沖池的功能,它能把與數據庫的連接放到池中,當一個客戶不再需要連接時,另一個客戶可以繼續使用它,這樣,應用服務器不必再次登錄到遠程數據庫服務器。可能有的讀者會想到,這個功能非常類似于TDatabase構件的KeepConnection屬性。不過,要注意的是,如果用了TDatabase構件的話,KeepConnection屬性最好設為False。
  MTS提供了強大的事務處理能力,它的“兩階段提交”技術使得應用程序能夠跨服務器處理事務。
  可以用TMTSDataModule類型的遠程數據模塊實現一個MTS服務器,這個MTS服務器能夠根據需要自動地激活或相反,換句話說,只有當遠程數據模塊接收到客戶的連接請求時才創建模塊的一個實例,這樣能夠最大程度地節省資源。
  由此可見,MTS服務器可以有兩種工作方式,一是單實例方式,一個實例能夠處理多個客戶的請求,不過,如果客戶較多的話,遠程數據模塊就成了瓶頸,制約著應用服務器的性能。二是多實例方式,每個客戶請求連接時都會創建遠程數據模塊的一個實例,這樣,幾個客戶就可以同時訪問數據庫而不需要排隊。
  為了發揮MTS的上述優勢,遠程數據模塊的實例必須做到與狀態無關,而IProvider接口又依賴于狀態信息,這就造成沖突。因此,TMTSDataModule類型的遠程數據模塊往往不用IProvider接口,而是自己創建一個接口來傳遞數據和申請更新。
  注意:使用MTS的時候,在遠程數據模塊的實例激活之前不能連接數據庫。
3.1.7 IDataBroker接口和IProvider接口
  應用服務器上的遠程數據模塊支持IDataBroker接口,當客戶程序與應用服務器連接以后,客戶程序上的MIDAS連接構件就查找IDataBroker接口。
  IDataBroker接口只實現了一個方法叫GetProviderNames,調用這個方法可以獲得一個列表,這個列表列出了應用服務器上的TDataSetProvider和TProvider構件。
  TClientDataSet的ProviderName屬性可以指定其中一個TDataSetProvider或TProvider構件。當客戶程序通過IDataBroker接口的GetProviderNames以及TClientDataSet的ProviderName屬性指定了應用服務器上的一個TDataSetProvider或TProvider構件后,只要客戶還在引用IProvider接口,遠程數據模塊的狀態就應該保持,這與MTS的許多特點是有沖突的,也會與單實例的CORBA服務器發生沖突。
  客戶程序與應用服務器之間通過IProvider接口交換數據,不過,大部分客戶程序并不直接使用IProvider接口,而是通過TClientDataSet的屬性和方法間接地使用IProvider接口。 不過,也可以通過Provider屬性獲得IProvider接口,然后直接訪問IProvider接口。
  下面這個表列出了IProvider接口的屬性和方法,同時列出了TProvider構件以及TClientDataSet構件中與之對應的屬性和方法。
IProviderTProviderTClientDatasetApplyUpdatesApplyUpdatesApplyUpdatesConstraints屬性Constraints客戶程序只能通過IProvider接口訪問這個屬性DataDataDataDataRequestDataRequest客戶程序只能通過IProvider接口訪問這個方法Get_ConstraintsConstraints客戶程序只能通過IProvider接口訪問這個方法Get_DataGet_Data用于實現Data屬性GetMetaDataGetRecords(Count = 0)內部使用GetRecordsGetRecords用于GetNextPacketResetReset內部使用Set_ConstraintsConstraints客戶程序只能通過IProvider接口訪問這個屬性SetParamsSetParams用于Params屬性
注意:IProvider接口的許多屬性和方法依賴于遠程數據模塊的狀態信息,正因為如此,在使用CORBA或MTS的應用程序中一般不要用IProvider接口。
3.2 選擇連接方式
  在客戶程序與應用服務器之間,Delphi 4提供了四種不同類型的連接方式或者說通訊協議,包括DCOM、TCP/IP、OLEnterprise和CORBA。這些不同的連接方式都各有利弊,到底選擇哪種連接方式,取決于客戶的數量、客戶的分布情況以及怎樣發布應用程序。
  DCOM是一種最直接的連接方式,它不需要專門的運行期軟件支持。不過,Windows 95 不支持DCOM,除非安裝了DCOM95程序。
  要使用MTS安全服務,最好使用DCOM連接方式。MTS的安全服務是基于角色的,當一個客戶通過DCOM訪問MTS時,DCOM會告訴MTS有關客戶的信息,MTS據此來決定客戶的角色。如果用其他連接方式,需要有專門的運行期軟件支持,客戶的調用首先被傳遞給這些運行期軟件而不是MTS,MTS就不能盡快指派角色。
  TCP/IP連接方式的適合范圍非常廣泛,例如,如果客戶程序要以ActiveForm的形式分布在Web上,最好采用TCP/IP連接方式,因為您無法肯定下載ActiveForm的計算機是否支持DCOM,而支持TCP/IP的環境是很普遍的。
  要使用TCP/IP連接方式,應用服務器端必須運行一個專門的運行期軟件ScktSrver.exe或ScktSrvc.exe,其中,ScktSrvc.exe只適合于Windows NT,可以作為一個服務在后臺運行。與DCOM連接方式不同的是,客戶的請求首先傳遞給ScktSrver.exe或ScktSrvc.exe,然后再創建遠程數據模塊的實例,而不是由客戶的調用直接創建遠程數據模塊的實例。客戶程序上的MIDAS連接構件通過IProvider接口與ScktSrvr.exe or ScktSrvc.exe通訊。
  不過,客戶程序很有可能在沒有正常釋放對IProvider 接口的引用之前出現異常,而TCP/IP連接方式無法檢測到這種情況,更無法通知應用服務器,因此,有可能造成應用服務器上的資源被占用后得不到釋放的后果。
  如果要在應用服務器端使用Business Object Broker,就要使用OLEnterprise連接方式。此時,應用服務器端和客戶端都要安裝OLEnterprise運行期軟件。
  Delphi 4是目前唯一支持CORBA的開發工具。基于CORBA的客戶程序和應用服務器可以與其他基于CORBA的應用程序無縫對接。要使用CORBA連接方式,需要ORB的支持,它提供了類似于Business Object Broker的功能。
3.3 創建應用服務器的一般步驟
  要創建一個多層Client/Server應用程序,首先要創建應用服務器,然后注冊或安裝應用服務器,只有應用服務器已注冊并且正在運行的情況下,才能創建客戶程序。對于客戶程序來說,既可以在設計期連接應用服務器,也可以在運行期連接應用服務器。
  注意:如果客戶程序與應用服務器不在同一個系統中,必須在客戶計算機上注冊或安裝應用服務器,這樣,在設計期就可以連接應用服務器。
  創建一個應用服務器與創建一個兩層的數據庫應用程序有些相似,主要的區別是,應用服務器需要提供IProvider接口,這一般是通過TDataSetProvider或TProvider構件提供的,也可以通過數據集構件如TTable的Provider屬性提供。創建應用服務器的一般步驟是:
  第一步是使用"File"菜單上的"New Application"命令開始一個新項目,然后使用File菜單上的New命令,選取Multi頁,如圖3.1所示。
  選擇一個遠程數據模塊。如果要創建一個COM自動化服務器,允許客戶通過DCOM、TCP/IP、OLEnterprise等方式訪問此服務器,選擇RemoteMod。如果要創建一個允許客戶通過MTS訪問的Active Library,選擇MTSData Module。如果要創建一個CORBA服務器,選擇Corba Data。
  第二步是把一個數據集構件如TTable、TQuery或TStoredProc放到遠程數據模塊上,并進行有關設置,使得它們能訪問遠程的SQL數據庫。盡量不要把TDatabase構件放到遠程數據模塊上,因為這可能引起名稱沖突。如果實在要用TDatabase構件來連接SQL數據庫,建議把TDatabase構件放到另一個數據模塊上,然后引用這個數據模塊的單元文件。
  第三步是把TDataSetProvider或TProvider構件放到遠程數據模塊上,有一個數據集構件,就要有一個TDataSetProvider或TProvider構件與之對應。然后,用鼠標右鍵單擊TDataSetProvider或TProvider構件,在彈出的菜單中選擇ExportFrom <Name> in Data Module命令,這是為了引出Provider接口,在類型庫中注冊。
  第四步是設置TDataSetProvider或TProvider構件的DataSet屬性指定要訪問的數據庫,實際上就是第二步所放的數據集構件。
  第五步是編寫代碼,實現商業規則。當然,這一步遠遠不是幾句話所能說清楚的。
  第六步是保存、編譯、注冊或安裝應用服務器。
  如果使用DCOM、TCP/IP、OLEnterprise作為通訊協議,應用服務器就好像一個自動化服務器一樣,必須像ActiveX或COM服務器那樣注冊。
  如果使用MTS,應用服務器是DLL而不是EXE,這時候不需要注冊應用服務器,而要把這個DLL作為MTS對象安裝到MTS包中。
  如果使用CORBA,可以不注冊但最好注冊。如果要使客戶程序對服務器接口的調用在運行期是動態確定的,就要在接口庫(Interface Repository)中安裝服務器的接口。如果要使客戶程序能自動激活應用服務器(如果還沒有運行的話),應用服務器就必須用OAD(Object Activation Daemon)注冊。
  第七步是如果應用服務器沒有使用DCOM,您必須安裝有關的運行期軟件,因為其他連接方式需要這些運行期軟件的支持。例如,對于TCP/IP來說,需要安裝ScktSrvr.exe或ScktSrvc.exe,后者只能運行在Windows NT環境下。對于OLEnterprise來說,需要安裝OLEnterprise運行期版本。對于CORBA來說,需要安裝VisiBroker ORB。
3.4 遠程數據模塊
  應用服務器的關鍵部件是遠程數據模塊。Delphi 4支持三種類型的遠程數據模塊,分別是TRemoteDataModule、TMTSDataModule、TCorbaDataModule。
3.4.1 TRemoteDataModule
  要加入一個TRemoteDataModule類型的遠程數據模塊,使用“File”菜單上的“New”命令,選取“Multitier”頁,雙擊“Remote Data Module”圖標,彈出“Remote Data Module Wizard”對話框,如圖3.2所示。?
  在“Class Name”框內鍵入遠程數據模塊的類名,不必以T打頭。Delphi 4將以此名生成一個TRemoteDataModule的派生類,并以此名生成有關接口。例如,假如在“Class Name”框內鍵入“MyDataServer”, 遠程數據模塊的類名就是TMyDataServer,它所實現的接口叫IMyDataServer,其祖先接口是IDataBroker。
  在“Threading Model”框內選擇一種線程模式。可以選“Single-threaded”、“Apartment-threaded”、“Free-threaded”或者“Both”。
  在“Instancing”框內選擇是否根據客戶的請求生成遠程數據模塊的多個實例,可以選“Single instance”或“Multiple instance”。
3.4.2 TMTSDataModule
  要加入一個TMTSDataModule類型的遠程數據模塊,使用“File”菜單上的“New”命令,選擇“Multitier”頁,雙擊“MTS Data Module”圖標,彈出“MTSData Module Wizard”對話框,如圖3.3所示。
  圖3.3 MTS Data Module對話框
  在“Class Name”框內鍵入遠程數據模塊的類名,不必以T打頭。Delphi 4將以此名生成一個TMTSDataModule的派生類,并以此名生成有關接口。例如, 假設在“Class Name”框內鍵入“MyDataServer”, 遠程數據模塊的類名就是TMyDataServer,它所實現的接口叫IMyDataServer,其祖先接口是IDataBroker。
  對于TMTSDataModule類型的遠程數據模塊來說,必須在“ThreadingModel”框內選擇一種線程模式。可以選“Single”、“Apartment”或者“Both”。在“Transaction Attributes”框內選擇事務屬性:
  如選擇“Requires a transaction”,每當客戶訪問遠程數據模塊的接口時,都與當前的事務是相關的。客戶不可能在事務中再申請一個新的事務。
  如選擇“Requires a new transaction”,每當客戶訪問遠程數據模塊的接口時,都自動開始一個新的事務。如選擇“Supports transactions”,遠程數據模塊可以用在事務的環境中,客戶訪問遠程數據模塊的接口時必須申請一個新的事務。
  如選擇“Does not support transactions”,遠程數據模塊不能用在事務的環境中。
  注意:MTS對象只能加入到ActiveX項目中,如果試圖在一個EXE項目中加入TMTSDataModule類型的遠程數據模塊,Delphi 4會顯示一個提示框,如圖3.4所示。
  圖3.4 一個提示框
3.4.3 TCORBADataModule
  要加入一個TCorbaDataModule類型的遠程數據模塊,使用“File”菜單上的“New”命令,選取“Multitier”頁,雙擊“CORBA Data Module”圖標,彈出“CORBA Data Module Wizard”對話框,如圖3.5所示。
  圖3.5 CORBA Data Module對話框
  在“Class Name”框內鍵入遠程數據模塊的類名,不必以T打頭。Delphi 4將以此名生成一個TCorbaDataModule的派生類,并以此名生成有關接口。例如,假設在“Class Name”框內鍵入“MyDataServer”, 遠程數據模塊的類名就是TMyDataServer,它所實現的接口叫IMyDataServer,其祖先接口是IDataBroker。
  在“Instancing”框內指定應用服務器怎樣創建遠程數據模塊的實例,可以選“Shared Instance”或者“Instance-Per-Client”。
  如果選“Shared Instance”,應用服務器只創建遠程數據模塊的一個實例來處理所有客戶的請求,因此,遠程數據模塊必須與狀態無關,換句話說,就是不能使用IProvider接口。
  如果選“Instance-Per-Client”,每當一個客戶試圖連接時,遠程數據模塊都會生成一個實例。只要客戶與應用服務器的連接沒有斷開,遠程數據模塊的實例就一直存在。這種模式下,允許使用IProvider接口。唯一要考慮的問題是,客戶程序有可能意外終止,導致沒有正常地斷開與應用服務器的連接。應用服務器為了避免不必要的資源浪費,可以定期地檢查客戶是否正在運行,如沒有,就手工把遠程數據模塊的實例刪掉。
  在“Threading Model”框內選擇一種線程模式。可以選“Single-threaded”、“Multi-threaded”。
3.5 Provider
  遠程數據模塊上往往要放一個或幾個TDataSetProvider或TProvider構件,用于提供IProvider接口。有時候,也可以不顯式地使用TDataSetProvider或TProvider構件,而是由數據集構件如TTable、TQuery或TStoredProc的Provider屬性間接地提供IProvider接口。
  顯式地使用TDataSetProvider或TProvider構件的好處是,可以直接控制數據包中包含哪些信息、應用服務器怎樣響應客戶的請求。如果顯式地使用了TDataSetProvider或TProvider構件,必須設置他們的DataSet屬性指定要訪問的數據集。
3.5.1 控制數據包中的字段
  要控制哪些字段包含到數據包中,首先要創建永久字段。以后,只有永久字段才加入到數據包中。如果不創建永久字段的話,數據集中的所有字段都將加入到數據包中。
  如果創建的永久字段中包含計算字段,由于計算字段的值是在運行期計算出來的,這些字段雖然也能加入到數據包中,但這些字段傳遞到客戶端后就變成只讀的。
  由于客戶程序很有可能要編輯修改數據,并且要把編輯修改后的數據申請更新到應用服務器上,因此,您創建的永久字段的數量不能太少,否則,很有可能出現重復的記錄。舉例來說,假設有一個學生成績表,由學號、姓名、語文成績、數學成績、歷史成績等字段組成,如果創建的永久字段中只包含語文成績、數學成績、歷史成績等字段,很有可能出現兩名學生的上述成績完全一樣,也就是說有重復的記錄,這是不允許的。
  如果實在不想使客戶程序看到某個字段,而如果沒有這個字段的話很有可能出現上述錯誤,這時候您可以讓這個字段(TField對象)的ProviderFlags屬性包含pfHidden元素,表示這個字段雖然加入到數據包中,但卻是隱含的,客戶看不到它。
  特別要注意的是,如果使用TQuery作為應用服務器上的數據集構件,SQL語句應當選擇足夠多的字段,即使客戶程序并不需要這么多字段,否則,就有可能出現上述錯誤。
3.5.2 Options屬性
  這個屬性是一個集合,用于設置有關打包和傳遞的選項。
  如果包含poFetchBlobsOnDemand元素,表示BLOB字段一般不放到包中,除非客戶端的TClientDataSet構件的FetchOnDemand屬性設為True或者顯式地調用FetchBlobs。
  如果包含poFetchDetailsOnDemand元素,表示嵌套表中的字段不放到包中,除非客戶端的TClientDataSet構件的FetchOnDemand屬性設為True或者顯式地調用FetchDetails。
  如果包含poIncFieldProps元素,表示把字段的屬性也放到包中,包括Alignment、MinValue、DisplayLabel、DisplayWidth、Visible、DisplayFormat、MaxValue、EditFormat、Currency、EditMask、DisplayValues等屬性。
  如果包含poCascadeDeletes元素,當父表中的某條記錄被刪除時就把子表中的相應記錄也刪除。
  如果包含poCascadeUpdates元素,當父表的關鍵字段的值變化時自動更新子表的記錄。
  如果包含poReadOnly元素,表示不允許“瘦”客戶向TDataSetProvider申請更新數據。
3.5.3 在數據包中加入自定義的信息
  當客戶端通過IProvider 接口調用DataRequest函數請求數據時將在應用服務器端觸發OnGetDataSetPropertiesevent事件,這樣,應用服務器就有機會在數據包中加入一些自定義的信息。客戶端可以調用GetOptionalParam來檢索這些信息。
  OnGetDataSetPropertiesevent事件是這樣聲明的:
  TGetDSProps = Procedure(Sender: TObject; DataSet: TDataSet; out Properties:OleVariant);
  其中,Properties參數是一個可變類型的數組,用于指定要加入的信息。Properties參數的每個元素由三部分組成:名稱、值和一個布爾數。Delphi 4定義了幾個標準的信息名稱,它們是UNIQUE_KEY、DEFAULT_ORDER、CHANGE_LOG、SERVER_COL、CONSTRAINTS、DATASET_CONTEXT、DATASET_DELTA、LCID、BDERECORD_X、TABLE_NAME、MD_FIELDLINKS、UPDATEMODE。程序示例如下:
Procedure TAppServer.Provider1GetDataSetProperties(Sender: TObject; DataSet: TDataSet; out Properties:OleVariant);
 Begin
  Properties := VarArrayCreate([0,1], varVariant);
  Properties[0] := VarArrayOf([/'TimeProvided/', Now, True]);?
  Properties[1] := VarArrayOf([/'TableSize/', DataSet.RecordCount, False]);
 End;
  上面這個程序中,加入了兩個自定義的信息,一個叫TimeProvided,它的值是當前的日期和時間,True表示這個信息可以由客戶端返回給應用服務器。另一個信息叫TableSize,它的值是數據集的記錄數,False表示這個信息不可以由客戶端返回給應用服務器。
  以后,當客戶端申請更新數據時,TDataSetProvider的OnUpdateData事件可以讀出數據包中的信息。程序示例如下:
Procedure TAppServer.Provider1UpdateData(Sender: TObject; DataSet: TClientDataSet);
  var WhenProvided: TDateTime;
 Begin
  WhenProvided := DataSet.GetOptionalParam(/'TimeProvided/');
   ...
 End;
3.5.4 響應客戶的數據請求
  在大多數的多層應用程序中,客戶請求數據是自動進行的,應用服務器對客戶請求的響應也是自動的,它自動地檢索數據、把數據打包,然后把數據包傳遞給客戶。
  應用服務器在把數據包傳遞給客戶之前,還有機會對其中的數據進行編輯 加工,例如,可以對其中敏感的數據加密,或者基于某種條件刪掉一些記錄。
  TDataSetProvider或TProvider構件的OnGetData事件可以讓您實現上述功能,程序示例如下:
Procedure TDBClientTest.ProviderGetData(DataSet: TClientDataSet);
Begin
  With DataSet Do
   Begin
    While not EOF Do
     Begin
      Edit;
      SensitiveData.AsString := DoEncrypt(SensitiveData.AsString);
      Post;
      Next;
     End;
   End;
End;
3.5.5 響應客戶的更新請求
  客戶程序通過調用ApplyUpdates 向應用服務器申請更新數據。當應用服務
器上的TDataSetProvider或TProvider構件收到客戶的更新請求后,就會觸發OnUpdateData事件,這樣您就有機會編輯數據包(Delta屬性)。退出處理OnUpdateData事件的句柄后,TDataSetProvider或TProvider構件就會把數據更新到遠程服務器上。
  更新是一條記錄一條記錄進行的。每一條記錄被更新前的一瞬間將觸發BeforeUpdateRecord事件,這樣您還有機會對數據進行檢查和修改。如果出現錯誤,就會觸發OnUpdateError事件。發生錯誤的原因通常是數據違反了服務器的糾錯規則,或者另一個客戶程序也修改了記錄,而且正好在前一個客戶已經申請更新的時候。
  上述錯誤既可以由應用服務器來處理,也可以回傳給客戶處理。有些錯誤可能需要用戶的介入,這就要客戶端在處理。
3.5.6 在更新數據庫之前編輯Delta數據包
  當應用服務器端調用ApplyUpdates向遠程服務器申請更新數據時將觸發OnUpdateData事件,這樣,應用服務器就有機會對將要更新的數據進行檢查,也可以對數據進行修改。OnUpdateData事件是這樣聲明的:
TProviderDataEvent = Procedure(Sender: TObject; DataSet: TClientDataSet) of object;
  其中,DataSet參數代表客戶程序上的TClientDataSet構件,這樣就可以訪問Delta屬性得到當前要更新的數據包。另外還有一個重要的屬性需要訪問,這就是UpdateStatus屬性,這個屬性表示Delta數據包的更新類型。程序示例如下:
Procedure TDataModule1.Provider1UpdateData(Sender:TObject;DataSet: TClientDataSet);
Begin
  With DataSet Do
  Begin
   First;
   While not Eof Do
    Begin
    If UpdateStatus = usInserted     then
     Begin
      Edit;
      FieldByName(/'DateCreated/').AsDateTime := Date;
      Post;
     End;
    Next;
    End;
  End;
End;
3.5.7 怎樣定位記錄
  在處理OnUpdateData事件的句柄中,除了可以檢查和修改Delta數據包外,還可以設置怎樣定位記錄或者說把哪些記錄更新到服務器上。
  默認情況下,應用服務器用自動生成的SQL UPDATE、INSERT或DELETE語句來把Delta數據包中寫到遠程服務器中,例如:
  UPDATE EMPLOYEES
   Set EMPNO = 748, NAME = /'Smith/', TITLE = /'Programmer 1/', DEPT = 52
  WHERE
   EMPNO = 748 and NAME = /'Smith/' and TITLE = /'Programmer 1/' and DEPT = 47
  除非另外指定,否則,數據包中的所有字段都將出現在UPDATE子句和WHERE部分,換句話說,就是用所有的字段去定位一條記錄。不過,也可以有選擇地排除一些字段,這就要用到UpdateMode屬性。這個屬性可以設為以下值:
.upWhereAll所有字段都用來定位記錄;
.upWhereChanged只有關鍵字段和變化了的字段用來定位記錄;
.upWhereOnly只有關鍵字段用來定位記錄。
  不過,UpdateMode屬性只能區分關鍵字段,但實際應用往往要復雜得多。例如,可能不想更新EMPNO字段,而且不想讓TITLE和DEPT字段出現在WHERE部分,這時候就要用到字段(TField對象)的ProviderFlags屬性,此屬性是一個集合,可以包含下列元素:
.pfInWhere該字段將不出現在自動生成的INSERT、DELETE和UPDATE語句的WHERE部分;
.pfInUpdate該字段將不出現在自動生成的UPDATE語句的UPDATE子句;
.pfInKey這個字段將出現在因為更新失敗而執行的SELECT語句的WHERE部分,SELECT語句用于選擇出錯的記錄的當前值;
.pfHidden這個字段將用來定位字段,但對客戶端是隱藏的。
  下面這個程序示例把EMPNO字段排除在UPDATE子句之外,把TITLE字段和DEPT字段排除在WHERE部分之外。
Procedure TDataModule1.Provider1UpdateData(Sender: TObject; DataSet: TClientDataSet);
Begin
With DataSet Do
Begin
FieldByName(/'EMPNO/').UpdateFlags := [ufInUpdate];
FieldByName(/'TITLE/').UpdateFlags := [ufInWhere];
FieldByName(/'DEPT/').UpdateFlags := [ufInWhere];
End;
End;?
3.5.8 在服務器端糾錯
  大多數數據庫管理系統(RDBMS)都實現了糾錯,以保證數據的完整和一致性。所謂糾錯,實際上就是預先指定一些規則,字段和記錄的值必須符合這些規則。
  大多數符合SQL-92的RDBMS都支持下列糾錯:
.NOT NULL字段必須有值;
.NOT NULL UNIQUE字段必須有值而且不能與其他記錄重復;
.CHECK字段的值必須在一個范圍內;
.CONSTRAINT在表格級對字段的值進行檢查;
.PRIMARY KEY指定一個或幾個字段作為關鍵字段;
.FOREIGN KEY指定一個或幾個字段引用其他表格。
  當然,不是所有的數據庫都支持上述糾錯,也有的服務器還支持其他糾錯。其實,許多數據庫桌面系統也支持糾錯,不過,在服務器端糾錯的優勢是,多個客戶程序可以共享服務器端的糾錯,而不必在每個客戶程序中重復一些代碼。
  應用服務器可以借用遠程數據庫服務器的糾錯規則,對客戶程序傳遞過來的數據進行糾錯,這就要用到Constraints屬性,只要把這個屬性設為True(默認)。
  如果不想借用遠程數據庫服務器的糾錯規則,應當把Constraints屬性設為False。
3.6 創建客戶程序的一般步驟
  在多層體系結構中,一個客戶程序至少要有一個TClientDataSet構件,它的作用是引入數據集。TClientDataSet是從TDataSet繼承下來的,它不需要依賴BDE。
  創建一個客戶程序的一般步驟是:
  第一步是使用“File”菜單上的“New Application”命令開始一個新的項目,然后使用“File”菜單上的“New”命令,再雙擊“Data Module”圖標加入一個數據模塊。
  第二步是把一個或幾個MIDAS連接構件如TDCOMConnection、TSocketConnection、TOLEnterpriseConnection、TCorbaConnection、TRemoteServer或TMIDASConnection加到數據模塊上。至于究竟選擇哪一種MIDAS連接構件,這取決于通訊協議。
  第三步是設置有關屬性指定和連接應用服務器,這與具體的MIDAS連接構件有關。有的MIDAS連接構件還有ObjectBroker屬性,可以指定一個TSimpleObjectBroker構件,這樣就可以動態地選擇應用服務器。
  第四步是把一個或幾個TClientDataSet構件放到數據模塊上,設置RemoteServer屬性指定一個MIDAS連接構件,設置ProviderName屬性指定應用服務器上的TDataSetResolver 或TProvider構件,這樣,客戶程序就可以通過IProvider接口與應用服務器通訊。
  第五步是把一個TDataSource構件放到數據模塊上,設置它的DataSet屬性指定TClientDataSet構件,再把一個數據控件如TDBGrid放到窗體上,設置它的DataSource屬性指定TDataSource構件。至此,一個簡單的客戶程序創建完畢。
3.7 與應用服務器連接
  要建立與應用服務器的連接,客戶程序必須使用一個或幾個MIDAS連接構件,這些構件可以在構件選項板的“MIDAS”頁上找到。
  不同類型的MIDAS連接構件使用不同的通訊協議,定位應用服務器的方式也不同。下面就詳細介紹這幾種MIDAS連接構件。
3.7.1 用DCOM來連接
  要使用DCOM方式來連接應用服務器,就要用到TDCOMConnection構件。
  TDCOMConnection構件的ComputerName屬性用于指定應用服務器所在的計算機。如果ComputerName屬性為空,TDCOMConnection就假設應用服務器與客戶程序在同一個計算機上,或者應用服務器在系統注冊表中可以找到。反過來說,如果應用服務器沒有在客戶端注冊,而且與客戶程序不在同一個計算機上,就須設置ComputerName屬性。
  即使應用服務器在客戶端注冊了,仍然可以設置ComputerName屬性重新指定一個另一臺計算機上的應用服務器。
  ComputerName屬性一般設為計算機的主機名或IP地址,如果指定的主機名或IP地址是非法的或者沒有找到,TDCOMConnection就會觸發異常。
  如果客戶程序需要在運行期間動態地選擇應用服務器,最好不要用ComputerName屬性來切換,而要加入一個TSimpleObjectBroker構件,再由ObjectBroker屬性指定這個TSimpleObjectBroker構件。
3.7.2 用TCP/IP連接
  要使用TCP/IP 方式來連接應用服務器,就需用到TSocketConnection構件。
  TCP/IP 方式是一種應用廣泛的連接方式,因為支持TCP/IP環境是相當普遍的。
  TSocketConnection用Address屬性或Host屬性來定位應用服務器所在的計算機,前者用于指定IP地址,后者用于指定主機名,這兩個屬性是互斥的,只需要設置其中一個。此外,還要設置Port屬性指定端口號,必須與應用服務器上運行的Scktsrver.exe或Scktsrvc.exe所使用的端口號一致,默認值是211。
3.7.3 用OLEnterprise連接
  要使用OLEnterprise方式連接應用服務器,就要用到TOLEnterpriseConnection構件。
  如果要直接連接應用服務器,不通過Business Object Broker,就要設置ComputerName屬性指定應用服務器所在的主機名,就好像DCOM方式一樣。
  如果要通過Business Object Broker連接應用服務器,就要設置BrokerName屬性指定Business Object Broker的名稱。
  ComputerName屬性和BrokerName屬性是互斥的,設置了其中一個,另一個就為清空。
3.7.4 用CORBA連接
  要使用CORBA方式連接應用服務器,就須用到TCorbaConnection構件。
  對于CORBA方式來說,只需要設置RepositoryID屬性標識CORBA數據模塊的接口,因為局域網中的智能代理(Smart Agent)會自動定位一個可用的應用服務器。
  不過,如果不想讓Smart Agent自動定位一個應用服務器,而是想指定一個特定的應用服務器,就要設置HostName屬性指定應用服務器的主機名或IP地址。
  TCorbaConnection構件可以通過兩種方式獲得應用服務器上CORBA數據模塊的接口:
  如果要使用靜態聯編方式,必須把_TLB.pas文件(由類型庫編輯器生成)加到客戶程序中。靜態聯編方式不僅能夠在編譯期進行類型檢查,而且運行速度較快。
  如果要使用動態聯編方式,CORBA數據模塊的接口必須用InterfaceRepository注冊。
3.7.5 標識服務器
  前面講的是怎樣定位應用服務器所在的計算機,現在要講定位了計算機后,怎樣標識應用服務器本身。
  如果使用DCOM、TCP/IP或OLEnterprise方式來連接應用服務器,您可以通過ServerName屬性或ServerGUID屬性來標識應用服務器。其中,ServerName用于指定應用服務器,實際上就是一個已注冊的OLE自動化對象名。ServerGUID屬性用于指定遠程數據模塊的全局識別號。如果合法設置了ServerName屬性,ServerGUID屬性會自動被設置。
  如果使用CORBA方式來連接應用服務器,就要用RepositoryID屬性來標識應用服務器上CORBA數據模塊的接口。
3.7.6 TSimpleObjectBroker
  如果客戶程序需要在運行期間動態地選擇應用服務器,最好用TSimpleObjectBroker構件來切換,而不使用ComputerName屬性。TSimpleObjectBroker能夠自動維護一個可用的應用服務器的列表。當MIDAS連接構件需要連接一個應用服務器時,它就向TSimpleObjectBroker提出申請,TSimpleObjectBroker一般會隨機提供一個應用服務器。如果TSimpleObjectBroker提供的應用服務器沒法工作,TSimpleObjectBroker會再換一個,一直到MIDAS連接構件與應用服務器建立了連接為止。
  一旦MIDAS連接構件與應用服務器建立了連接,它會自動把應用服務器的有關情況保存到有關屬性中,如ComputerName、Address或Host等,因為MIDAS連接構件有可能會斷開連接,以后又要再次連接,這時候就不必再向TSimpleObjectBroker提出申請。
  在使用OLEnterprise或CORBA方式的情況下,不要用TSimpleObjectBroker構件,因為這兩種方式都有自己專門的中介服務。
3.7.7 開始連接
  進行了上述有關設置后,現在就可以連接應用服務器了。不過,在連接應用服務器之前,最好還要設置TClientDataSet的RemoteServer屬性指定一個MIDAS連接構件,再設置ProviderName屬性指定應用服務器中的TDataSetResolver 或TProvider構件。
  當客戶程序試圖訪問IProvider接口時,就會自動建立與應用服務器的連接,例如,把TClientDataSet的Active屬性設為True。
  當然,也可以通過MIDAS連接構件的Connected屬性來連接或斷開連接。
  在將要與應用服務器建立連接之前,會觸發BeforeConnect事件。當建立了與應用服務器的連接后,會觸發AfterConnect事件。
3.7.8 斷開連接
  當進行下列操作時會使連接斷開:
.把Connected屬性設為False。
.關閉客戶程序或MIDAS連接構件被刪除。
.MIDAS連接構件的ServerName、ServerGUID、ComputerName、Host、Address等屬性修改后,也會使原有的連接斷開,再與基于新的應用服務器重新建立連接。
  注意:盡量不要使用修改ComputerName、Host等屬性的方式來切換應用服務器,最好用TSimpleObjectBroker,或者使用幾個MIDAS連接構件分別建立連接。
  與應用服務器的連接將要斷開之前會觸發BeforeDisconnect事件,連接真正斷開之后,會觸發AfterDisconnect事件。
3.8 調用服務器上的接口
  通過TClientDataSet的Provider屬性可以獲得IProvider接口。其實,大部分情況下并不需要直接調用IProvider接口,因為對IProvider接口的調用已經封裝在TClientDataSet的屬性和方法中,唯一的例外是,DataRequest函數只能通過IProvider接口調用。
  通過MIDAS連接構件的AppServer屬性可以獲得應用服務器上遠程數據模塊的接口,通過此接口可以調用遠程數據模塊的方法,例如:
  MyConnection.AppServer.SpecialMethod(x,y);
  這種調用方式是動態聯編的,也就是說,編譯器并不檢查SpecialMethod的參數,甚至連有沒有SpecialMethod它都不管。由于編譯器不進行類型檢查,在運行期實際調用時有可能失敗。而且,動態聯編沒有靜態聯編的執行速度快。
  如果用DCOM或CORBA作為通訊協議連接應用服務器,最好采用靜態聯編方式來訪問遠程數據模塊的接口,方法就是用特定的接口類型對AppServer屬性進行強制類型轉換。假設遠程數據模塊的接口叫IMyAppServer(它的上級是IDataBroker),程序示例如下:
With MyConnection.AppServer as IMyAppServer Do SpecialMethod(x,y);
  上面這行代碼適合于DCOM方式,下面這行代碼適合于CORBA方式:
With IUnknown(MyConnection.AppServer) as IMyAppServer Do SpecialMethod(x,y);
  對于DCOM方式來說,要使用靜態聯編方式調用遠程數據模塊的接口,它的類型庫必須在客戶端注冊。要注冊類型庫,可以調用匼DELPHI4//BIN目錄中的TREGSVR.EXE。
  對于CORBA方式來說,要使用靜態聯編方式調用遠程數據模塊的接口,必須在客戶程序中引用類型庫編輯器生成的_TLB.pas文件。
  對于TCP/IP或OLEnterprise方式來說,沒法使用真正的靜態聯編方式來調用遠程數據模塊的接口,不過,可以通過遠程數據模塊的調度接口來改善性能,程序示例如下:
  varTempInterface: IMyAppServerDisp;
Begin
TempInterface := MyConnection.AppServer;
...
TempInterface.SpecialMethod(x,y);
...
End;
  要通過調用接口訪問遠程數據模塊,必須在客戶程序中引用類型庫編輯器生成的_TLB.pas文件。
3.9 在客戶端糾錯
  SQL Explorer可以把服務器端的糾錯和默認表達式引入到一個數據字典中,這樣,通過應用服務器上的基于BDE的數據集,客戶端就可以借用服務器端的規則對數據糾錯,這就是所謂的客戶端糾錯。如果用戶在客戶端輸入或修改的數據違反了規則,這些數據就不會傳遞到應用服務器端,更不會傳遞到RDBMS端。
  由此可見,在客戶端糾錯可以避免把錯誤的數據傳遞給遠程數據庫服務器,從而節省了網絡上不必要的傳輸,因為這些數據即使傳遞給遠程數據庫服務器,也會被退回。
  不過,有時候也需要暫時禁止在客戶端糾錯。例如,假設有一項糾錯規則需要基于字段的最大值,而客戶端一次只能檢索若干條記錄,也就是說,客戶端統計出來的最大值與服務器端統計出來的最大值有可能不一樣。如果客戶端使用了過濾,也有可能使客戶端統計出來的最大值與服務器端統計出來的最大值不一樣。上述情況下,就需要暫時禁止糾錯。
  要暫時禁止糾錯,可以調用TClientDataSet的DisableConstraints,DisableConstraints實際上是使一個內部的引用計數加1,當這個引用計數大于0,就不允許糾錯。
  要重新允許糾錯,可以調用TClientDataSet的EnableConstraints,EnableConstraints實際上是使一個內部的計數減1,當這個計數減到0時,就可以重新允許糾錯。
3.10 更 新 數 據
  當客戶程序從應用服務器檢索到數據,就在內存中建立這些數據的副本。用戶對數據進行編輯修改后,TClientDataSet專門用一個Delta屬性存儲變化了記錄,包括更新、刪除和插入的記錄。為了使修改了的數據永久化,就要向數據庫申請更新數據。
3.10.1 更新數據的一般步驟
  首先,客戶程序要調用ApplyUpdates函數向應用服務器提出申請,ApplyUpdates函數將通過IProvider接口把Delta屬性傳遞給應用服務器。
  應用服務器收到客戶程序的申請后,再向遠程數據庫服務器提出申請,并且把被遠程數據庫服務器認為出錯的記錄暫時緩存起來。應用服務器上的TDataSetProvider或TProvider構件把出錯的記錄返回給客戶程序,其中包括錯誤信息和錯誤代碼。
  客戶程序收到這些出錯的記錄后,可以進行核對和修改,然后繼續更新。注意:如果應用服務器端使用MTS類型的遠程數據模塊,就無法提供IProvider接口,這種情況下,必須通過遠程數據模塊的接口直接申請更新數據。?
3.10.2 ApplyUpdates函數
  當用戶修改了數據后,應當調用ApplyUpdates函數向應用服務器申請更新數據。
  ApplyUpdates函數只有一個MaxErrors參數,用于指定一個最大錯誤數,如果出錯的記錄數超過了這個參數的值,此次更新就停止。如果MaxErrors參數設為0,只要應用服務器發現有一個錯誤的記錄,更新操作就停止。如果MaxErrors參數設為-1,當應用服務器發現有錯誤的記錄,就嘗試更新下一個記錄,等所有的記錄都嘗試過以后才返回。
  ApplyUpdates函數將返回實際遇到的錯誤數,同時,應用服務器將返回那些有錯誤的記錄。
3.10.3 核對出錯的記錄
  當應用服務器把出錯的記錄返回給客戶程序時,將在客戶端觸發OnReconcileError事件,這樣,您就有機會對記錄進行核對并修改。OnReconcileError事件是這樣聲明的:
TReconcileErrorEvent = Procedure (DataSet: TClientDataSet; E: EReconcileError; UpdateKind:TUpdateKind; var Action: TReconcileAction) of object;
  其中,DataSet參數是TClientDataSet構件名,籍此可訪問數據集中某字段的NewValue、OldValue、CurValue等屬性,從而分析有關出錯的原因。通過DataSet參數也能夠調用TClientDataSet的方法,但不能進行可能導致數據被修改的操作,否則,可能會引起死循環。
  E參數是一個指向EReconcileError對象的指針,從中可以提取出有關錯誤信息和導致錯誤的原因。
  UpdateKind參數表示是哪種操作導致了錯誤,可以是以下值:ukModify(修改)、ukInsert(插入)、ukDelete(刪除)。
  Action參數用于決定在退出處理OnReconcileError事件的句柄后是否繼續更新數據集,可以設為下列值:raSkip、raAbort、raMerge、raCorrect、raCancel、raRefresh。下面這個程序示例演示了怎樣調用RecError單元中的一個對話框:
Procedure TForm1.ClientDataSetReconcileError(DataSet:TClientDataSet;E:EReconcileError; UpdateKind:TUpdateKind, var Action TReconcileAction);
  Begin
  Action := HandleReconcileError(DataSet, UpdateKind, E);
  End;
3.10.4 刷新記錄
  客戶程序把數據在內存中建立一個副本,并工作于這個副本,而其他用戶有可能已經修改了數據,也就是說,內存中的數據已不是數據庫中的實際數據。
  為了使內存中的數據是當前最新的,可以調用TClientDataSet的Refresh。不過,調用Refresh前要保證客戶端沒有未確定的修改,換句話說,就是客戶端的日志中沒有記載任何修改,否則會觸發異常。
  不過,TClientDataSet的RefreshRecord可以不管當前有沒有未決的修改,它可以刷新當前記錄,而日志中記載的修改繼續保留。
  注意:調用RefreshRecord有可能帶來沖突。因此,調用RefreshRecord之前,最好還是檢查一下當前是否有未決的修改,如果有的話,就觸發異常,程序示例如下:
If ClientDataSet1.UpdateStatus <> usUnModified then
Raise Exception.Create(/'You must apply updates before refreshing the current record./');
ClientDataSet1.RefreshRecord;
3.10.5 從應用服務器獲取參數
  下列兩種情況下,客戶程序需要從應用服務器獲得參數:
.客戶程序需要知道存儲過程的輸出參數。
.客戶程序需要初始化TQuery或TStoredProc的輸入參數。
  要從應用服務器獲得參數,調用TClientDataSet的FetchParams函數,這個函數將返回有關參數并作為Params屬性的值。在設計期也可以獲取參數,辦法是:用鼠標右鍵單擊TClientDataSet構件,在彈出的菜單中選擇“Fetch Params”命令。
  注意:無論是在運行期調用FetchParams函數,還是在設計期使用“FetchParams”命令,客戶端必須已經與應用服務器連接并且獲得了IProvider接口,因為這些參數是由應用服務器上的TDataSetProvider或TProvider構件提供的,它們的DataSet屬性必須指定了TQuery構件或TStoredProc構件。
  也可以在客戶端設置Params屬性,然后把參數傳遞給應用服務器。
3.11 自定義應用服務器
  MIDAS的結構是非常靈活的,可以自定義應用服務器以適應實際的需要。您可以從兩個方面來自定義應用服務器,一是擴展遠程數據模塊的接口,二是使用自定義的數據集。
  遠程數據模塊是應用服務器的核心部件,它提供了應用服務器與客戶程序通訊的基本接口,除非客戶程序直接調用IProvider接口。
  如果應用服務器使用MTS類型的遠程數據模塊,客戶程序只能通過遠程數據模塊的接口與應用服務器進行通訊。如果試圖繞過MTS代理,就會使事務無效,特別是,由于MTS類型的遠程數據模塊可以與狀態無關,這時候如果繞過MTS代理,有可能導致程序崩潰。
  同樣,單實例模式的CORBA數據模塊也是與狀態無關的,也存在著上述的問題。
  為了使客戶程序能夠方便地訪問MTS或CORBA數據模塊(因為這時候沒有IProvider接口),您必須對遠程數據模塊的接口進行擴展,添加一些方法讓客戶程序調用。
  MTS或CORBA數據模塊的接口都是從IDataBroker繼承下來的。要向接口中加入新的屬性或方法,您可以有兩種操作方式:
  一是使用“Edit”菜單上的“Add to Interface”命令,彈出“Add to Interface”對話框,如圖3.6所示。
  圖3.6 “Add to Interface”對話框
  在“Declaration”框內鍵入屬性或方法的聲明,單擊OK按鈕,Delphi 4就會把屬性或方法加入到遠程數據模塊的接口中。
  二是使用“View”菜單上的“Type Library”命令打開類型庫編輯器,如圖3.7所示。
  在類型庫編輯器中選擇接口節點,例如圖3.7中的IXXH,然后單擊工具欄 上的“New Method”按鈕或“New Property”按鈕。加入了一個新的成員后,要設置有關屬性。要說明的是,對于CORBA類型的遠程數據模塊的接口來說,許多屬性是無效的。
  對于基于COM的遠程數據模塊(TRemoteDataModule或TMTSDataModule)來說,新的成員將出現在接口的實現單元和類型庫的描述文件中。
  對于基于CORBA的遠程數據模塊(TCorbaDataModule)來說,新的成員除了加到接口的實現單元中外,還會自動生成一個_TLB單元。如果客戶程序需要訪問基于CORBA遠程數據模塊,必須引用這個單元。另外,您可以讓類型庫編輯器生成IDL腳本,然后用Interface Repository和Object Activation Daemon來注冊接口。
  客戶程序可以通過MIDAS連接構件的AppServer屬性獲取遠程數據模塊的接口,然后通過接口調用遠程數據模塊的方法。
  注意:如果使用MTS的話,每一個加入到接口中的方法必須在最后調用SetComplete,告訴MTS事務可以結束了。例如,下面的GetCustomerRecords函數用于獲取記錄:
Function TMyRemoteDataModule.GetCustomerRecords(MetaData: Boolean; outRecsOut: Integer):OleVariant;
Begin
Try
If MetaData then Result := CustomerProvider.GetRecords(0, RecsOut);
Else Result := CustomerProvider.GetRecords(-1, RecsOut);
SetComplete;
ExceptSetAbort;
End;
End;
在客戶端,可以這樣調用GetCustomerRecords函數:
ClientDataSet1.Data := CorbaConnection1.AppServer.GetCustomerRecords(False, RecsOut);
再舉個例子,下面的ApplyCustomerUpdates函數用于申請更新數據:
Function TMyRemoteDataModule.ApplyCustomerUpdates(Delta: OleVarant; MaxErrors: Integer; outErrorCount: Integer); OleVariant;
Begin
Try
Result := CustomerProvider.ApplyUpdates(Delta, MaxErrors, ErrorCount);
SetComplete;
ExceptSetAbort;
End;
End;
  在客戶端,可以這樣調用ApplyCustomerUpdates函數:
With ClientDataSet1 Do
Begin
CheckBrowseMode;
If ChangeCount > 0 then
Reconcile(MyConnectionComponent.AppServer.ApplyCustomerUpdates(Delta,MaxErrors, ErrCount));
End;
在應用服務器上,一般要使用基于BDE的數據集構件來引入數據,TDataSetProvider或TProvider構件的DataSet屬性指定此數據集構件。不過,默認情況下,TDataSetProvider或TProvider構件能夠用動態生成的SQL語句直接與遠程的數據庫服務器通訊,而不需要借助于基于BDE的數據集構件,這樣做的好處是減少了一個環節。
  不過,TDataSetProvider或TProvider構件有時候也需要直接向應用服務器上的數據集構件申請更新數據,因為應用服務器上使用的有可能不是基于BDE的數據集,而是TClientDataSet或自定義的數據集。這時候需要把ResolveToDataSet屬性設為True。
  如果能確定應用服務器不需要用到BDE,最好用TDataSetProvider構件而不是TProvider構件,TDataSetProvider不需要依賴BDE, 有利于發布和安裝應用服務器。?
3.12 多層體系結構下的事務
  當客戶程序向應用服務器申請更新數據,TDataSetProvider或TProvider構件會自動把更新數據的例程加上一層事務的外套,換句話說,就是處于事務的控制之下。如果出錯的記錄數沒有超過MaxErrors參數,就向遠程數據庫服務器正式提交此次事務,否則就滾回。
  為了更好地支持事務,可以在應用服務器端用TDatabase構件來管理數據庫的連接和事務,它的用法與兩層體系結構一樣。
  如果在應用服務器端使用MTS,就可以獲得更強大的事務處理能力。MTS事務可以延伸到所有的商業邏輯,而不局限于數據庫訪問。
  另外,MTS的“兩階段提交”技術,使MTS能夠跨多個數據庫處理事務。要說明的是,“兩階段提交”技術目前只在Oracle和MS-SQL Server中完全實現,如果要跨數據庫進行事務,而其中有的數據庫不是Oracle和MS-SQL Server,就有可能出錯。
  正如前面提到的那樣,如果要使用MTS類型的遠程數據模塊,應當擴展它的接口,用自定義的方法來封裝MTS的事務功能。?
3.13 把客戶程序設計為ActiveForm
  Delphi 4可以把分布式的數據庫結構引申到Internet/Intranet上,把客戶程序作為ActiveForm嵌入到網頁中讓人們下載,然后在當地執行。
  Internet/Intranet上的應用服務器必須支持DCOM或TCP/IP連接方式,同樣,設計成ActiveForm的客戶程序也必須支持DCOM或TCP/IP連接方式,因為下載ActiveForm的計算機上可能沒有安裝OLEnterprise或CORBA運行期軟件。
  在設計客戶程序的界面時,要用ActiveForm代替一般的窗體。為此,首先要使用“File”菜單上的“New”命令打開“New Items”對話框,選取“ActiveX”頁,雙擊“ActiveForm”圖標,打開ActiveForm向導,如圖3.8所示。?
  單擊“OK”按鈕,Delphi 4就創建一個ActiveX項目,這個項目中只有一個空白的ActiveForm,下面的步驟就象設計一般的“瘦”客戶一樣。
  以ActiveForm的形式設計好一個“瘦”客戶程序后,還需要把它發布到Web服務器上,供人們下載。為此,首先要使用“Project”菜單上的“Web DeploymentOptions”命令打開“Web Deployment Options”對話框,然后設置有關Web發布的選項,主要是指定ActiveForm在Web服務器上的URL。最后,使用“Project”菜單上的“WebDeploy”命令把ActiveForm發布到Web服務器上。
  為了測試這個Active窗體,可以用一個Web瀏覽器如IE下載嵌入了ActiveForm的網頁。如果客戶程序通過DCOM與應用服務器連接,Windows 95中需要安裝支持DCOM的程序——DCOM95,而Windows NT 4.0則不需要。


--------------------------------------------------------------------------------
?
熟悉 MIDAS
你可能已經注意到了DELPHI中的縮寫MIDAS,然而你可能和多數DELPHI的開發者一樣,并未意識到這種技術帶來的巨大進步。Jani Jervinen 描述了MIDAS-Multi-tier Distributed Application Services Suite是如何讓你把CLIENTSERVER應用轉換成多層應用的。
? ? 傳統的CLIENTSERVER應用已經延續了很長時間,多數的DELPHI開發者通過編寫應用程序來提高水平。CLIENTSERVER技術對需要訪問遠端數據的簡單數據庫應用來說是個好的解決方案。
? ? 例如,DELPHI可以很方便的訪問ORACLE或MICROSOFT SQL SERVER 數據庫。然而,隨著應用的增大,維持它會消耗越來越多的資源。同時,隨著應用程序的用戶增加,性能會成為重要的問題。 ?
? ? 而且,INTERNET時代需要系統每天24小時不間斷運行。在CLIENTSERVER應用中,后端數據庫管理系統DBMS扮演著重要角色。如果服務器處理客戶端的請求失敗,會導致客戶端應用的失敗。
把多層變為視圖
? ? 為解決這個問題可以用多種解決方案。分布式就是其中一種,我確信你已經不止一次聽到過這個詞。然而,分布式應用的一個必須認真考慮的問題就是你必須對前面提到的問題非常熟悉。
? ? 雖然有很多方式來建立分布式應用,DELPHI的用戶仍然會對MIDAS技術感興趣。MIDAS已經成為DELPHI3及其后繼版本的一部分。如果你用的是DELPHI CLIENTSERVER的企業版,MIDAS的強大優勢仍然值得另外的花費。
? ? 通過MIDAS,你可以把CLIENTSERVER應用分發到層,每一層實現一種邏輯上獨立的功能。多層應用的優點就是你可以很方便的替換應用中的每一層,因為每一層都不知道其他層是如何實現它們的功能的。并且,每一層通過特殊的方式和其他層通訊,并只對其他層提供的服務感興趣,而不是如何提供這種服務。
最簡單的分布式應用是三層式應用。在三層式應用中,客戶端的角色就是顯示數據。中間層處理所有的交易邏輯,第三層是數據庫服務器。如圖1所示。


圖1
為什么多層?
? ? 多層應用的優勢在你比較傳統應用和多層應用的維護時會變得很明顯。當你不得不修改CLIENTSERVER應用時,你需要重建一個新的版本并重新部署它。如果有很多客戶,你可能需要整周的時間來做這件事。
? ? 例如,假設你在創建一個用于計算銷售人員工資的工資表的應用。工資依賴于銷售人員的銷售情況,突然,老板告訴你計算方法需要修改。
? ? 在CLIENTSERVER應用中,你需要修改客戶端的程序。然而,如果你已經把交易邏輯(計算方法)從用戶接口中分離出來,你就只需要替換中間層的應用。比較修改100個客戶端和只修改中間層的花費,不言而喻。
? ? 然而,節省開支并不是建立多層應用的唯一理由。當第一層只是用來顯示數據,建立INTERNET應用就會變得更經濟。例如,我的很多客戶問我如何創建他們最好的基于WEB的應用。我通常告訴他們解決這個問題的最好方法就是首先用MIDAS建立一個多層應用的版本,然后為它創建一個WEB用戶接口。當然,分布式應用對多層式來說解決起來并非難事,但是一旦你體會到了這項技術的優越性,你就會很快知道它什么時候有用或無用。


MIDAS 很容易創建
? ? 現在你已經知道了為什么多層式對傳統的CLIENTSERVER應用來說是種很好的選擇了,現在是更進一步學習MIDAS的時候了。第一個版本的MIDAS是在DELPHI 3 CLIENTSERVER中發布,所以在DELPHI 5 企業版中MIDAS已經是第三個版本了。雖然MIDAS最初是設計用于DELPHI,它同樣可以用于DELPHI的姊妹產品C++BUILDER。和DELPHI的其他應用開發一樣,MIDAS的開發就是把控件放到FORM上。所有的MIDAS控件可以在組件模板‘MIDAS’(見圖2)中找到。由于MIDAS是用來在層間傳送數據庫數據,多數控件跟共享和操作數據有關。表1對DELPHI 5 中所見到的控件給出了簡單的描述。


圖2
表 1. DELPHI 5 中的MIDAS 控件。
控件 描述
TClientDataSet (Enhanced TTable replacement to be used in MIDAS client applications)用于MIDAS的客戶端應用的增強的Ttable替代控件
TDCOMConnection (Connection component for using DCOM)用DCOM連接的連接控件
TSocketConnection (Lightweight connection component that uses TCPIP)用TCPIP連接的方便的連接控件。
TDataSetProvider (Component to "export" a dataset from a MIDAS application server)從MIDAS應用服務器‘輸出’數據集的控件
SimpleObjectBroker (Simple component to help in load-balancing MIDAS applications)用于MIDAS應用的負載平衡的簡單控件
TWebConnection (Component for tunneling database data through HTTP)通過HTTP訪問數據庫數據的控件
TCorbaConnection (Can be used to connect CORBA and MIDAS applications)可以用來連接CORBA和MIDAS應用


? ? 在MIDAS應用中,客戶端應用通過應用服務器來取數據,應用服務器依次從數據庫取數據。應用服務器把數據庫中的數據打包并返回給客戶端。在MIDAS術語中,應用服務器成為提供數據給客戶端。
? ? MIDAS 提供了三種不同的連接方式(CORBA 連接是特殊的并且通常不包含在內),一個應用服務器可以同時支持所有連接。應用服務器使用常規的數據庫控件來訪問數據庫,然后用一個數據provider控件來允許客戶端應用來訪問數據。客戶端應用使用連接控件來連接應用服務器。連接方式依賴于客戶端的應用—例如,簡單連接可以用TCPIP,然而復雜的客戶端應用就要用DCOM連接。
? ? 客戶端的應用在使用連接控件的同時使用Client Dataset控件,它的作用就象平常的Ttable一樣。例如,Client Dataset控件支持合計字段,過濾器,和主從關系,使你對MIDAS感到適應。
建立一個應用服務器
? ? 現在你可以用你的MIDAS知識來建立一個真正意義上的應用了。和所有的MIDAS應用一樣,最好從應用服務器端開始開發。首先,啟動DELPHI并選擇一個New Application。改變自動生成的FORM的大小以使它可以放一個Tlable控件,然后在它上面放一個Tlable控件,改變它的Caption屬性,比如改為‘My MIDAS Server’。 Label 和 form 的大小并不重要,因為它們只是用來在屏幕上指示MIDAS應用服務器。通常應用服務器根本不包含任何用戶接口,但為了學習,最好有個簡單的用戶接口。它只是讓你來控制服務器。
? ? 我在建立MIDAS應用服務器時最喜歡的一步就是設置窗口的背景顏色。改變主窗體的顏色對于確信應用服務器處于運行狀態來說是個很好的方法。當然,這只是在你創建服務器的用戶接口時才有用。
現在,選擇綠顏色,并設置label的字體顏色為白色,以增加對比度。然后從FILE菜單中選擇NEW,圖3顯示了NEW對話框中的選項,從中你要選擇的是 位于Multitier 頁面中的Remote Data Module圖標。我在此不想詳細描述,但是必須是一個Remote Data Module,因為MIDAS 是建立在COM基礎之上的。因此,你的Remote Data Module 會在后面的例子中自動說明。這里有三個不同的data modules 可用的原因就是MIDAS服務器可以用于不同的環境。例如,你的MIDAS應用可以和CORBA應用通訊。


圖3
現在回到代碼中,按New Items 對話框的OK按鈕后,你就會看到圖4所示的向導。因為remote data module是一個COM對象,你需要為它指定一個 CoClass 名稱。這個名稱在你的系統中必須是唯一的,所以我選擇‘MyGreenMIDASServer’為它的名稱。其他選項用它的缺省值,你需要參考表2來了解這些選項的用途。


圖4
表 2. Remote Data Module向導中的選項
選項 說明
CoClass Name (Specifies the name of the remote data module object.)標識remote data module對象的名字
Instancing (Controls how the remote module is created when the application server runs. For example, Single Instance means that every client connects to its own instance of the application server.)在應用服務器運行時控制remote module如何創建。例如,Single Instance表示每個客戶端用自己的實例連接到應用服務器
Threading model (Indicates how client calls are passed to the remote data module. For example, Free means that multiple threads are used.)說明客戶端的調用如何傳送到remote data module.例如,FREE表示使用多線程。


提供數據集
? ? 當remote data module建立好之后,你就可以給它增加普通的database components。我在例子中訪問的是DBDEMOS別名數據庫中的Biolife表,所以一個Ttable控件就夠了。
? ? MIDAS服務器可以支持所有從TdataSet類繼承而來的數據庫控件。事實上,這意味著你可以選擇用ADO,BDE,甚至InterBase Express來連接數據庫。你選擇的每種數據庫連接對客戶端應用來說都沒什么不同,所以當你決定用ADO替換BDE時客戶端不必做任何修改。這是不是很COOL?
? ? 為了訪問Biolife表,在data module上放一個Ttable控件,并把它和我們熟知的‘fish fact’表連接。為了確保它工作正常,可以activate Ttable 控件來測試數據庫的連接。然而,你隨后應該馬上關閉表,因為讓表保持OPEN狀態會耗盡寶貴的服務器資源。關閉表是安全的,因為MIDAS在需要時會自動激活控件。
然后,在表單上放一個TdataSetProvider控件(你可以在MIDAS控件頁面中找到這個控件)。你要做的就是設置dataset provider的DataSet屬性為Ttable 控件。做完這些以后,你就完成了應用服務器的建立,并可以保存整個project。我用RemoteDM,MainForm,和GreenServer來分別為remote data module,the green form,和project來命名。
? ? 當你保存了整個project以后,就可以馬上編譯并運行了。這是個重要的步驟。第一次運行應用服務器時會自動在Windows注冊表中注冊它。記住:事實上remote data modules是COM對象,所以注冊表中必須包含EXE文件的位置(目錄)信息,甚至更多。


客戶端應用
? ? 現在,你的應用服務器已經完成,可以建立客戶端的應用了。從View 菜單中選擇Project Manager選項,右擊project group 并選擇Add New Project。當熟悉的New Items對話框打開后,選擇Application圖標并選擇OK。一個新的表單會出現在IDE中。注意服務器端和客戶端應用要在同一個項目組中。這使得MIDAS的應用開發更容易,因為你可以很容易的在兩個項目中切換。另一種選擇就是打開兩個DELPHI,但我更喜歡第一種方法。
? ? 接下來,改變客戶端應用的表單大小使它可以放一個grid。從MIDAS控件頁面中選擇一個TDCOMConnection控件放到表單上,這個控件是你可以選擇的四種連接控件中的一個。如果你用的是Windows 95 或r 98,你需要在繼續前先閱讀補充說明‘Windows 95, 98 and DCOM’。
? ? 回到Object Inspector,設置連接控件的ServerName屬性為GreenServer.MyGreenMIDASServer。注意在Object Inspector中的列表中會自動顯示在本系統中注冊的應用服務器。當你選擇了服務器后,DELPHI會自動填寫ServerGUID屬性。你可以忽略GUID除非你對COM感興趣。
? ? 現在,你可以讓ComputerName屬性為空。這意味著客戶端在本機尋找應用服務器。如果你想在其他機器上運行服務器,可以指定ComputerName屬性值為其他機器名稱。
為了測試連接,置Connected屬性的值為True。然后你就會看到你的應用服務器開始在后臺運行(見圖5),這就是COM的奇妙之處。事實上,你已經看到了just-in-time activation的所有狀況。


圖5
? ? 接下來,用鼠標從從控件模板中選擇一個TclientDataSet控件放到表單上。設置它的RemoteServer屬性為connection控件的名字。選擇ProviderName屬性為應用服務器上的provider控件的名字。注意什么會發生:應用服務器會和你的客戶端應用通訊,并告訴它服務器提供什么數據集。
? ? 隨后的事就很熟悉了:放一個TdataSource和一個TDBGrid到表單上,把data source連接到client dataset,把grid連接到data source。現在,設置client dataset的Active屬性為True。如果順利的話,你就會在grid中看到Biolife表中的數據。這些數據沒有任何值得懷疑。
實施MIDADS應用
? ? 一旦你完成了MIDAS應用的創建,我確信就你會對實施它感興趣。雖然實施MIDAS應用會很EASY,你也要按它的基本步驟進行,它們可以一次完成。
? ? 在開始服務器應用之前,你需要兩個在SERVER上運行的DLL。這兩個DLL是STDVCL40.DLL 和 MIDAS.DLL。當安裝服務器時,你最好把這兩個文件拷貝到Windows系統目錄下,這樣你就可以很方便的找到它們。拷貝好之后,這兩個DLL還需要注冊。
? ? 注冊是個和COM有關的事情,并且是必須的,以使系統可以找到DLL。通常使用REGSVR32.EXE來注冊,它和Windows同時被安裝。然而,有些系統沒有這項功能,你就需要用TregSvr,它是DELPHI中的一個演示程序。你可以在DELPHI的Demos//ActiveX//TregSvr目錄下找到。
? ? 盡管這些工具用起來很方便,最好的方式還是用安裝工具生成一個安裝包。例如,我使用InstallShield(DELPHI專業版中提供)來創建一個安裝包,并設置MIDAS的DLLs為‘self-registering’。
給代碼授權
? ? 在你實施MIDAS應用之前,一定要注意MIDAS并非是免費軟件。當你實施你的MIDAS應用時,通常需要購買一個實施許可證。開發MIDAS應用程序不需要許可,因為,在DELPHI 5的企業版中已經有了。
? ? 在http://shop.borland.com/上的實施許可-$299.95允許你在一臺機器上實施你的MIDAS應用。這個許可對客戶端的連接數沒有限制,所以MIDAS事實上很便宜。
? ? 為了使MIDAS讓更多人買的起,Inprise公司對許可協議保留了一個例外。在DELPHI的DEPLOY.TXT文件中:"A server deployment license is not required… in an application in which the client and server reside on the same machine." (‘服務器的許可證不需要…當客戶端和服務趨端在同一臺機器上時’。)
更多MIDAS
? ? MIDAS讓你可以用你現在的DELPHI技術方便的開發分布式應用,而不必涉及DCOM 或 CORBA的更深內容。而且,你可以用DELPHI的開發方式建立你的應用程序,同時速度會很快。
? ? 盡管在一篇文章中并不能描述MIDAS的所有內容,我希望你可以在此學到最基本的技術。我建議對TclientDataSet控件花點時間,相信你不會對它的一些特性失望。
? ? 作為進一步的練習,我建議你測試不同的連接方式。例如,使用TsocketConnection,它可以讓你和服務器建立很lightweight的連接。同時,你可能想學習IappServer接口,它是MIDAS技術的基礎。
? ? 如果你想對MIDAS了解的更多,我建議你訪問Borland公司的WEB站點http://community.borland.com/。特別是Dan Miser寫的文章更值得一讀。同時要記住:將來不管遇到什么問題,也不會阻擋DELPHI開發者在MIDAS領域的旅程。
下載 JARVINEN.ZIP


補充說明: Windows 95, 98 and DCOM
? ? 如果你用的是Windows 95或98,你必須確保DCOM已經安裝。缺省情況下,Windows 95或98不包含DCOM,但Windows NT 和 Windows 2000已經安裝了。要在Windows 95上安裝DCOM,從DELHPI的安裝CD上運行DCOM95.EXE(這個文件位于安裝目錄下)。或者,瀏覽http://www.microsoft.com/com,并下載最新的安裝包。要在Windows 98上安裝,從相同的網址上下載最新的DCOM安裝版本。?
--------------------------------------------------------------------------------


其實也就一個主要問題,為什么保存數據時非得要先斷開連接,再重新連接后才能正常保存?


百思不得其解!
--------------------------------------------------------------------------------
在非多層的情況下,更新數據往往先判斷事務是否正在進行,多層不了解。不知是否有類似情況


procedure TForm1.ADODataSet1BeforePost(DataSet: TDataSet);
begin
if not ADOConnection1.InTransaction ?then
begin
ADOConnection1.BeginTrans;
end;


procedure TForm1.ADODataSet1AfterPost(DataSet: TDataSet);
begin
if ADOConnection1.InTransaction ?then
begin
ADOConnection1.CommitTrans;
end;
end;


--------------------------------------------------------------------------------
?
-- ?作者:HUWEIJI
-- ?發布時間:2005-8-3 19:38:33
-- ?
出錯的提示是:
 Cannot create new transaction because capacty was exceeded.
可是除了BeforeUpdateRecord自動開啟的事務外,沒有任何其他事務也照樣出問題。
我用的是DBExpress,以前用ADO時也沒有問題。

 保存的過程是 SqlConnection.Connection:=False;
? ? ? ? ? ? ? ? SqlConnection.Connction:=True;
?保存;
        SqlConnection.Connection:= False;

少了一項都可能出問題。

如果只保存一個ClientDataSet中的數據是沒有問題的,當不止一個時,比如說父子表,就不知道怎么辦了。
去大富翁也查不出所以然來,真是郁悶 出處:?http://www.360doc.com/content/12/0817/17/7662927_230712619.shtml

轉載于:https://www.cnblogs.com/lrl45/p/5135450.html

總結

以上是生活随笔為你收集整理的Delphi:ClientDataset+TDataSetProvider的数据保存问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

亚洲自偷精品视频自拍 | 性欧美牲交xxxxx视频 | 中文字幕乱码人妻二区三区 | 国产乱人伦偷精品视频 | 日韩少妇内射免费播放 | 在线播放免费人成毛片乱码 | 久久久精品456亚洲影院 | 99久久精品无码一区二区毛片 | 中文毛片无遮挡高清免费 | 亚洲中文字幕无码中字 | 亚洲啪av永久无码精品放毛片 | 97资源共享在线视频 | aⅴ亚洲 日韩 色 图网站 播放 | 国产一区二区三区影院 | 日韩精品无码免费一区二区三区 | 天堂久久天堂av色综合 | 中文字幕+乱码+中文字幕一区 | 中文字幕无码乱人伦 | 欧美乱妇无乱码大黄a片 | 久久人人爽人人爽人人片av高清 | 久久国语露脸国产精品电影 | 亚洲中文字幕无码一久久区 | 亚洲春色在线视频 | 国产精品国产三级国产专播 | 欧美熟妇另类久久久久久不卡 | 中文字幕无码人妻少妇免费 | 亚洲 a v无 码免 费 成 人 a v | 色窝窝无码一区二区三区色欲 | 亚洲午夜久久久影院 | 欧美乱妇无乱码大黄a片 | 大乳丰满人妻中文字幕日本 | 久久久久99精品成人片 | 人妻少妇被猛烈进入中文字幕 | 久久精品中文字幕大胸 | 欧美35页视频在线观看 | 动漫av一区二区在线观看 | 亚洲精品综合一区二区三区在线 | 亚洲精品一区国产 | 亚洲日韩av片在线观看 | 欧美性猛交xxxx富婆 | 又大又黄又粗又爽的免费视频 | 丰满诱人的人妻3 | 国产欧美熟妇另类久久久 | 久久aⅴ免费观看 | 国产成人av免费观看 | 特级做a爰片毛片免费69 | 在线观看国产午夜福利片 | 成人免费视频一区二区 | 欧美zoozzooz性欧美 | 人人妻人人澡人人爽精品欧美 | 大肉大捧一进一出视频出来呀 | 无码人中文字幕 | 中文字幕无码日韩专区 | 一本久久伊人热热精品中文字幕 | 精品久久久久久人妻无码中文字幕 | 99久久人妻精品免费二区 | 亚洲а∨天堂久久精品2021 | 无码一区二区三区在线 | 国产乱人无码伦av在线a | 国产精品美女久久久久av爽李琼 | 大乳丰满人妻中文字幕日本 | 精品久久久无码中文字幕 | 亚洲人成无码网www | 成人精品一区二区三区中文字幕 | 亚洲成熟女人毛毛耸耸多 | 久久久亚洲欧洲日产国码αv | 国内揄拍国内精品少妇国语 | 亚洲成av人在线观看网址 | 亚洲国产av美女网站 | 好男人社区资源 | 99久久精品无码一区二区毛片 | 亚洲熟熟妇xxxx | 5858s亚洲色大成网站www | 人人妻人人澡人人爽欧美精品 | 亚洲色成人中文字幕网站 | 国产一区二区三区影院 | 中文字幕无码热在线视频 | 老子影院午夜伦不卡 | 久久久久久久久888 | 国产成人精品三级麻豆 | 久久久久99精品成人片 | 99久久婷婷国产综合精品青草免费 | 无码人妻丰满熟妇区毛片18 | 国产真实伦对白全集 | 免费无码一区二区三区蜜桃大 | 成人无码精品一区二区三区 | 欧美人与禽zoz0性伦交 | 色偷偷人人澡人人爽人人模 | а√天堂www在线天堂小说 | 午夜性刺激在线视频免费 | 老司机亚洲精品影院无码 | 亚洲综合在线一区二区三区 | 最新国产乱人伦偷精品免费网站 | 好男人www社区 | 一个人免费观看的www视频 | 久久精品无码一区二区三区 | 狂野欧美性猛xxxx乱大交 | 日韩精品无码一区二区中文字幕 | 老司机亚洲精品影院无码 | 领导边摸边吃奶边做爽在线观看 | 国产在线无码精品电影网 | 国产精品永久免费视频 | 精品成人av一区二区三区 | 西西人体www44rt大胆高清 | 无码纯肉视频在线观看 | 国产精品高潮呻吟av久久4虎 | 又大又黄又粗又爽的免费视频 | 在线亚洲高清揄拍自拍一品区 | 日日躁夜夜躁狠狠躁 | 白嫩日本少妇做爰 | 无码福利日韩神码福利片 | 伊在人天堂亚洲香蕉精品区 | 精品国产精品久久一区免费式 | 国内丰满熟女出轨videos | 国产成人无码a区在线观看视频app | 无码人妻丰满熟妇区毛片18 | 亚洲欧洲日本无在线码 | 国产精品久久久久久久9999 | 给我免费的视频在线观看 | 欧美精品一区二区精品久久 | 在线成人www免费观看视频 | 免费乱码人妻系列无码专区 | 7777奇米四色成人眼影 | 一本精品99久久精品77 | 日韩成人一区二区三区在线观看 | 久久人人爽人人爽人人片ⅴ | 国产美女极度色诱视频www | 国精产品一区二区三区 | 欧美 丝袜 自拍 制服 另类 | 中国大陆精品视频xxxx | 亚洲精品国产精品乱码视色 | 永久免费精品精品永久-夜色 | 国产成人无码av片在线观看不卡 | 精品人妻av区 | 日日麻批免费40分钟无码 | 99久久婷婷国产综合精品青草免费 | 欧美性猛交内射兽交老熟妇 | 欧美日韩人成综合在线播放 | 亚洲一区二区三区四区 | 久久无码专区国产精品s | 伊人久久大香线蕉午夜 | 狠狠cao日日穞夜夜穞av | 99国产精品白浆在线观看免费 | 特黄特色大片免费播放器图片 | 国产精品无码久久av | 巨爆乳无码视频在线观看 | 国内精品九九久久久精品 | 牲欲强的熟妇农村老妇女视频 | 久久国产劲爆∧v内射 | 人人妻人人澡人人爽人人精品浪潮 | 国产 浪潮av性色四虎 | 日日鲁鲁鲁夜夜爽爽狠狠 | 午夜无码区在线观看 | 成在人线av无码免观看麻豆 | 性色欲情网站iwww九文堂 | 无套内射视频囯产 | 国产精品久久久久久无码 | 亚洲日本va中文字幕 | 久精品国产欧美亚洲色aⅴ大片 | 国产av一区二区精品久久凹凸 | 天下第一社区视频www日本 | 欧美人与禽猛交狂配 | 超碰97人人做人人爱少妇 | 国产综合色产在线精品 | 久久99精品久久久久久 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 国产av无码专区亚洲awww | 少妇被粗大的猛进出69影院 | 日韩欧美成人免费观看 | 亚洲国产av美女网站 | 美女毛片一区二区三区四区 | 中文字幕乱妇无码av在线 | 美女扒开屁股让男人桶 | 蜜臀av在线观看 在线欧美精品一区二区三区 | 一本久道久久综合婷婷五月 | 黑人玩弄人妻中文在线 | 国产欧美亚洲精品a | 人人妻人人藻人人爽欧美一区 | 国产精品久久久久9999小说 | 性色欲情网站iwww九文堂 | 亚洲精品午夜国产va久久成人 | 正在播放东北夫妻内射 | 亚洲日韩中文字幕在线播放 | 国产精品久久久久无码av色戒 | 女人色极品影院 | 18无码粉嫩小泬无套在线观看 | 久久99久久99精品中文字幕 | 99国产欧美久久久精品 | 99视频精品全部免费免费观看 | 日韩少妇白浆无码系列 | 免费无码午夜福利片69 | 久久久久国色av免费观看性色 | 久久伊人色av天堂九九小黄鸭 | 亚洲国产精品无码一区二区三区 | 娇妻被黑人粗大高潮白浆 | 白嫩日本少妇做爰 | 午夜丰满少妇性开放视频 | 一本久道久久综合婷婷五月 | 日本欧美一区二区三区乱码 | 国产性生大片免费观看性 | 国产午夜无码精品免费看 | 欧美 丝袜 自拍 制服 另类 | 国产无遮挡又黄又爽免费视频 | 国产午夜精品一区二区三区嫩草 | 国产乱码精品一品二品 | 国产人成高清在线视频99最全资源 | 九月婷婷人人澡人人添人人爽 | 狠狠cao日日穞夜夜穞av | 国产精品久久久久久亚洲毛片 | 精品国产一区av天美传媒 | 丰满少妇熟乱xxxxx视频 | 国内综合精品午夜久久资源 | 色 综合 欧美 亚洲 国产 | 久久人妻内射无码一区三区 | 男女下面进入的视频免费午夜 | 日本一区二区三区免费高清 | 亚洲国产日韩a在线播放 | 在线观看欧美一区二区三区 | 国内精品人妻无码久久久影院 | 又大又紧又粉嫩18p少妇 | 久久精品国产精品国产精品污 | 丰腴饱满的极品熟妇 | 大地资源中文第3页 | 国产成人无码av在线影院 | 香港三级日本三级妇三级 | 国产无套粉嫩白浆在线 | 双乳奶水饱满少妇呻吟 | 99麻豆久久久国产精品免费 | 日日摸天天摸爽爽狠狠97 | 日产精品99久久久久久 | 毛片内射-百度 | 国产真实伦对白全集 | 亚洲综合另类小说色区 | 麻豆国产人妻欲求不满 | 精品国产av色一区二区深夜久久 | 久久99国产综合精品 | 亚洲精品午夜国产va久久成人 | 日日碰狠狠丁香久燥 | 国产精品久久久久9999小说 | 欧美性猛交xxxx富婆 | 亚洲人成网站色7799 | 奇米影视7777久久精品 | 天天av天天av天天透 | 玩弄人妻少妇500系列视频 | 午夜精品久久久久久久久 | 永久免费观看美女裸体的网站 | 大屁股大乳丰满人妻 | 国内精品久久久久久中文字幕 | 亚洲爆乳大丰满无码专区 | a在线亚洲男人的天堂 | 国产精品久久久久久亚洲毛片 | 欧美三级不卡在线观看 | 午夜福利一区二区三区在线观看 | 麻豆av传媒蜜桃天美传媒 | 鲁一鲁av2019在线 | 亚洲国产综合无码一区 | 欧美性生交xxxxx久久久 | 亚洲成a人片在线观看无码3d | 九月婷婷人人澡人人添人人爽 | 日本一卡2卡3卡四卡精品网站 | 97久久国产亚洲精品超碰热 | 伊在人天堂亚洲香蕉精品区 | 国内少妇偷人精品视频 | 亚洲一区二区三区无码久久 | 国产精品办公室沙发 | 亚洲精品一区二区三区在线 | 人妻天天爽夜夜爽一区二区 | 国产97在线 | 亚洲 | 少妇的肉体aa片免费 | 九九热爱视频精品 | 黑人巨大精品欧美一区二区 | 思思久久99热只有频精品66 | 日韩无码专区 | 亚洲无人区午夜福利码高清完整版 | 水蜜桃色314在线观看 | 天下第一社区视频www日本 | 老熟妇乱子伦牲交视频 | 99久久久国产精品无码免费 | 国产深夜福利视频在线 | 久久国产精品_国产精品 | aⅴ亚洲 日韩 色 图网站 播放 | 女人被男人爽到呻吟的视频 | 呦交小u女精品视频 | 中文字幕无码日韩欧毛 | 一本色道婷婷久久欧美 | 免费无码肉片在线观看 | 人人爽人人澡人人高潮 | 风流少妇按摩来高潮 | 丰满妇女强制高潮18xxxx | 国产精品.xx视频.xxtv | 亚洲区小说区激情区图片区 | 一本久久伊人热热精品中文字幕 | 久久国产精品精品国产色婷婷 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 久久久久久av无码免费看大片 | 性色av无码免费一区二区三区 | 国产成人综合在线女婷五月99播放 | 377p欧洲日本亚洲大胆 | 中国大陆精品视频xxxx | 成 人影片 免费观看 | 日本欧美一区二区三区乱码 | 天天拍夜夜添久久精品大 | 一本加勒比波多野结衣 | 国产成人无码区免费内射一片色欲 | 又色又爽又黄的美女裸体网站 | 中文无码成人免费视频在线观看 | 成人动漫在线观看 | 精品国产一区二区三区av 性色 | 亚洲色偷偷男人的天堂 | 东京热无码av男人的天堂 | 精品无码av一区二区三区 | 国产欧美精品一区二区三区 | 国产99久久精品一区二区 | 啦啦啦www在线观看免费视频 | 国产偷自视频区视频 | 日本www一道久久久免费榴莲 | 日日天干夜夜狠狠爱 | 性开放的女人aaa片 | 狂野欧美性猛交免费视频 | 夜夜躁日日躁狠狠久久av | 亚洲欧美日韩国产精品一区二区 | 国产乡下妇女做爰 | 久久精品国产大片免费观看 | 日产精品高潮呻吟av久久 | 色综合久久久无码网中文 | 国产一区二区不卡老阿姨 | 久久人人爽人人人人片 | 特级做a爰片毛片免费69 | 亚洲精品一区二区三区四区五区 | 领导边摸边吃奶边做爽在线观看 | a片免费视频在线观看 | 色情久久久av熟女人妻网站 | 成人三级无码视频在线观看 | 国产舌乚八伦偷品w中 | 亚洲成av人在线观看网址 | 国产精品久久国产三级国 | 亚洲精品综合五月久久小说 | 日本一卡二卡不卡视频查询 | 久久亚洲中文字幕精品一区 | 无码av中文字幕免费放 | 日韩精品乱码av一区二区 | 久久99精品久久久久久 | 熟女少妇人妻中文字幕 | 国产xxx69麻豆国语对白 | 国内丰满熟女出轨videos | 人人妻人人澡人人爽人人精品浪潮 | 成 人影片 免费观看 | 久久精品国产大片免费观看 | 熟妇人妻无码xxx视频 | 六月丁香婷婷色狠狠久久 | 中国女人内谢69xxxxxa片 | 色噜噜亚洲男人的天堂 | 欧美精品国产综合久久 | 波多野42部无码喷潮在线 | 最近的中文字幕在线看视频 | 亚洲国产精品久久人人爱 | 亚洲热妇无码av在线播放 | 国内综合精品午夜久久资源 | 无码人妻丰满熟妇区毛片18 | 日日躁夜夜躁狠狠躁 | 老熟妇仑乱视频一区二区 | 久久人人爽人人爽人人片ⅴ | 亚洲成色www久久网站 | 欧美成人免费全部网站 | 亚洲另类伦春色综合小说 | 亚洲伊人久久精品影院 | 亚洲国产一区二区三区在线观看 | 中文无码精品a∨在线观看不卡 | 牲欲强的熟妇农村老妇女 | 夜夜高潮次次欢爽av女 | 亚洲欧美综合区丁香五月小说 | 老头边吃奶边弄进去呻吟 | 亚洲自偷自偷在线制服 | 国产精品亚洲专区无码不卡 | 亚洲成a人片在线观看无码 | 成人无码影片精品久久久 | 日韩视频 中文字幕 视频一区 | 最近的中文字幕在线看视频 | 久久人人爽人人人人片 | 丰满妇女强制高潮18xxxx | 欧美第一黄网免费网站 | 亚洲国产高清在线观看视频 | 久久国产精品精品国产色婷婷 | 成人影院yy111111在线观看 | 国产亚洲tv在线观看 | 狂野欧美性猛交免费视频 | 无码人妻精品一区二区三区下载 | 亚洲 日韩 欧美 成人 在线观看 | 无码毛片视频一区二区本码 | 亚洲熟妇自偷自拍另类 | 久久天天躁夜夜躁狠狠 | 国产9 9在线 | 中文 | 99国产精品白浆在线观看免费 | 久久国语露脸国产精品电影 | 综合人妻久久一区二区精品 | 久久国产劲爆∧v内射 | 欧美熟妇另类久久久久久多毛 | 精品久久综合1区2区3区激情 | 日韩精品无码免费一区二区三区 | 国产卡一卡二卡三 | 欧美丰满熟妇xxxx性ppx人交 | 青春草在线视频免费观看 | 亚洲热妇无码av在线播放 | 日本乱人伦片中文三区 | 大地资源中文第3页 | 四虎国产精品一区二区 | 亚洲日韩乱码中文无码蜜桃臀网站 | 欧美 日韩 亚洲 在线 | 精品国产青草久久久久福利 | 成人片黄网站色大片免费观看 | 国产口爆吞精在线视频 | 性欧美熟妇videofreesex | 午夜福利试看120秒体验区 | 亚洲 欧美 激情 小说 另类 | 亚洲国产精品无码久久久久高潮 | 久久精品国产大片免费观看 | 377p欧洲日本亚洲大胆 | 国产香蕉尹人综合在线观看 | 精品一区二区三区无码免费视频 | 国产97人人超碰caoprom | 成人免费视频一区二区 | 中文毛片无遮挡高清免费 | 国产成人无码av片在线观看不卡 | 性史性农村dvd毛片 | 澳门永久av免费网站 | 性欧美疯狂xxxxbbbb | 性生交片免费无码看人 | 亚洲s色大片在线观看 | 久久精品国产大片免费观看 | 亚洲国产精品一区二区美利坚 | 日本丰满熟妇videos | 99久久人妻精品免费一区 | 好男人www社区 | 亚洲热妇无码av在线播放 | 久久精品中文闷骚内射 | 97se亚洲精品一区 | 久久精品女人的天堂av | 日本熟妇乱子伦xxxx | 日本精品久久久久中文字幕 | 中文字幕乱码中文乱码51精品 | 性欧美熟妇videofreesex | 夜精品a片一区二区三区无码白浆 | 久久久久99精品国产片 | 岛国片人妻三上悠亚 | 无码国产色欲xxxxx视频 | 色综合天天综合狠狠爱 | 小泽玛莉亚一区二区视频在线 | 日日摸夜夜摸狠狠摸婷婷 | 久久亚洲a片com人成 | 免费网站看v片在线18禁无码 | 久久久精品456亚洲影院 | 蜜臀av在线播放 久久综合激激的五月天 | 中文无码精品a∨在线观看不卡 | 精品人妻av区 | 牛和人交xxxx欧美 | 国产精品久久久久久亚洲影视内衣 | 精品无码av一区二区三区 | 亚洲乱码国产乱码精品精 | 国产人妻精品午夜福利免费 | 日韩人妻少妇一区二区三区 | 亚洲国产精品久久人人爱 | 激情内射日本一区二区三区 | 亚洲热妇无码av在线播放 | 国产真实夫妇视频 | 人人澡人人妻人人爽人人蜜桃 | 99久久人妻精品免费一区 | 性欧美牲交在线视频 | 在线精品国产一区二区三区 | 日本一卡2卡3卡四卡精品网站 | 国产精品高潮呻吟av久久4虎 | 狂野欧美性猛xxxx乱大交 | 狠狠躁日日躁夜夜躁2020 | 欧美真人作爱免费视频 | 秋霞成人午夜鲁丝一区二区三区 | 国内精品九九久久久精品 | 国产一精品一av一免费 | 国产精品无码mv在线观看 | 国产精品欧美成人 | 亚洲人成网站色7799 | 欧美黑人乱大交 | 无码人妻精品一区二区三区不卡 | 久久97精品久久久久久久不卡 | 熟女少妇在线视频播放 | 女高中生第一次破苞av | 欧美大屁股xxxxhd黑色 | 国产精品办公室沙发 | 午夜丰满少妇性开放视频 | 在线亚洲高清揄拍自拍一品区 | 欧美精品无码一区二区三区 | 久久精品人妻少妇一区二区三区 | 欧美 日韩 人妻 高清 中文 | 精品国产一区av天美传媒 | 97精品人妻一区二区三区香蕉 | 欧洲熟妇精品视频 | 日本爽爽爽爽爽爽在线观看免 | 国产激情精品一区二区三区 | 午夜不卡av免费 一本久久a久久精品vr综合 | 亚洲色在线无码国产精品不卡 | 波多野结衣一区二区三区av免费 | 1000部夫妻午夜免费 | 风流少妇按摩来高潮 | 久久久久99精品国产片 | 亚洲人成影院在线无码按摩店 | 性色欲网站人妻丰满中文久久不卡 | 日日摸夜夜摸狠狠摸婷婷 | 亚洲欧美精品aaaaaa片 | 国产av一区二区精品久久凹凸 | 四虎影视成人永久免费观看视频 | 久久这里只有精品视频9 | 成在人线av无码免观看麻豆 | 国产成人综合在线女婷五月99播放 | 99久久久国产精品无码免费 | 无码午夜成人1000部免费视频 | 亚洲欧美国产精品久久 | 成年美女黄网站色大免费视频 | 欧美日韩久久久精品a片 | 日本www一道久久久免费榴莲 | 美女张开腿让人桶 | 青青青爽视频在线观看 | 丝袜足控一区二区三区 | 国产xxx69麻豆国语对白 | 乱人伦人妻中文字幕无码久久网 | 国产特级毛片aaaaaaa高清 | 欧美变态另类xxxx | 中文字幕中文有码在线 | 国产美女精品一区二区三区 | 国产精品资源一区二区 | 国内精品人妻无码久久久影院蜜桃 | 色婷婷香蕉在线一区二区 | 日日麻批免费40分钟无码 | 丰满少妇人妻久久久久久 | 色综合久久中文娱乐网 | 一本无码人妻在中文字幕免费 | 国产精品久久久一区二区三区 | 欧美日本日韩 | 国产午夜无码精品免费看 | 在线播放亚洲第一字幕 | 老司机亚洲精品影院无码 | 久久99久久99精品中文字幕 | 奇米影视7777久久精品 | 又色又爽又黄的美女裸体网站 | 精品国产精品久久一区免费式 | 精品国精品国产自在久国产87 | 熟女少妇人妻中文字幕 | 午夜精品一区二区三区在线观看 | 成人无码视频在线观看网站 | 欧洲美熟女乱又伦 | 18禁止看的免费污网站 | 亚洲自偷精品视频自拍 | 99麻豆久久久国产精品免费 | 久久午夜无码鲁丝片午夜精品 | 日日噜噜噜噜夜夜爽亚洲精品 | 免费看男女做好爽好硬视频 | 色婷婷欧美在线播放内射 | 精品国产av色一区二区深夜久久 | 少妇性荡欲午夜性开放视频剧场 | 亚洲综合无码久久精品综合 | 欧美人与禽zoz0性伦交 | 亚洲成a人片在线观看无码 | 亚洲色无码一区二区三区 | 日本精品人妻无码免费大全 | 鲁鲁鲁爽爽爽在线视频观看 | 日本护士毛茸茸高潮 | 我要看www免费看插插视频 | 久久天天躁狠狠躁夜夜免费观看 | 国产精品久久国产精品99 | 国产成人精品无码播放 | 成人欧美一区二区三区黑人 | 两性色午夜免费视频 | 亚洲色欲久久久综合网东京热 | 亚洲精品一区二区三区在线观看 | 成人一在线视频日韩国产 | 国产午夜无码精品免费看 | 国产精品二区一区二区aⅴ污介绍 | 亚洲区欧美区综合区自拍区 | 风流少妇按摩来高潮 | 国产人妻精品午夜福利免费 | 1000部啪啪未满十八勿入下载 | 国产亚洲视频中文字幕97精品 | 色欲综合久久中文字幕网 | 最新版天堂资源中文官网 | 国产又粗又硬又大爽黄老大爷视 | 成人无码视频免费播放 | 国产又爽又黄又刺激的视频 | 久久亚洲国产成人精品性色 | 国产精品亚洲一区二区三区喷水 | 国产精品理论片在线观看 | 岛国片人妻三上悠亚 | 东北女人啪啪对白 | 无码人妻精品一区二区三区不卡 | 亚洲精品中文字幕久久久久 | 波多野42部无码喷潮在线 | 装睡被陌生人摸出水好爽 | 国产疯狂伦交大片 | 免费观看激色视频网站 | 亚洲精品一区二区三区在线 | 在线天堂新版最新版在线8 | 成人免费视频一区二区 | 在线看片无码永久免费视频 | 2020最新国产自产精品 | 日本一本二本三区免费 | 亚洲の无码国产の无码步美 | 麻花豆传媒剧国产免费mv在线 | 欧美日本免费一区二区三区 | 少妇高潮喷潮久久久影院 | 日韩av激情在线观看 | 亚洲成色在线综合网站 | 99久久精品午夜一区二区 | 亚洲精品一区三区三区在线观看 | 无套内谢的新婚少妇国语播放 | 国产成人无码专区 | 自拍偷自拍亚洲精品被多人伦好爽 | 亚洲爆乳大丰满无码专区 | 日本熟妇大屁股人妻 | 一本久道久久综合婷婷五月 | 久久99精品久久久久久 | 成人性做爰aaa片免费看 | 亚洲精品一区三区三区在线观看 | ass日本丰满熟妇pics | 99久久精品国产一区二区蜜芽 | 亚洲男人av香蕉爽爽爽爽 | 日本丰满护士爆乳xxxx | 亚洲日韩av一区二区三区中文 | 在线看片无码永久免费视频 | 免费中文字幕日韩欧美 | 无码国产激情在线观看 | 久久久国产一区二区三区 | 男人扒开女人内裤强吻桶进去 | 精品国产av色一区二区深夜久久 | 国产成人无码一二三区视频 | 国产做国产爱免费视频 | 国产一区二区三区日韩精品 | 国产乡下妇女做爰 | 久久综合九色综合97网 | 日韩精品久久久肉伦网站 | 无码人妻丰满熟妇区毛片18 | 免费乱码人妻系列无码专区 | 亚洲精品中文字幕久久久久 | 亚洲熟妇色xxxxx欧美老妇 | 无码任你躁久久久久久久 | 天堂а√在线地址中文在线 | 国产精品人妻一区二区三区四 | 无码精品人妻一区二区三区av | 国产真实伦对白全集 | 欧美亚洲国产一区二区三区 | 丰满人妻翻云覆雨呻吟视频 | 亚洲精品成人福利网站 | 精品偷自拍另类在线观看 | 亚洲精品午夜国产va久久成人 | 欧美性猛交xxxx富婆 | 国内少妇偷人精品视频免费 | 久久久无码中文字幕久... | 亚洲色无码一区二区三区 | 欧美精品免费观看二区 | 兔费看少妇性l交大片免费 | 国产无遮挡又黄又爽又色 | 又大又硬又爽免费视频 | 亚洲小说春色综合另类 | 人人爽人人澡人人人妻 | 欧美人与物videos另类 | 成人欧美一区二区三区黑人免费 | 无码人妻黑人中文字幕 | 一个人看的www免费视频在线观看 | 欧美人与善在线com | 亚洲日韩av片在线观看 | 天天摸天天碰天天添 | 装睡被陌生人摸出水好爽 | 午夜精品久久久久久久久 | 在教室伦流澡到高潮hnp视频 | 学生妹亚洲一区二区 | 亚洲国产精品无码一区二区三区 | 久久国产精品_国产精品 | 久久久久亚洲精品男人的天堂 | 色老头在线一区二区三区 | 国产精品久久久久久亚洲毛片 | 一本久久a久久精品vr综合 | 国产在热线精品视频 | 久久国语露脸国产精品电影 | 亚洲精品久久久久久久久久久 | 精品久久久久久人妻无码中文字幕 | 日本xxxx色视频在线观看免费 | 久青草影院在线观看国产 | 精品国产一区二区三区av 性色 | 麻豆人妻少妇精品无码专区 | 成人无码视频免费播放 | 熟妇人妻激情偷爽文 | 久久亚洲日韩精品一区二区三区 | 国产午夜亚洲精品不卡下载 | 少妇无码av无码专区在线观看 | 性史性农村dvd毛片 | 亚洲狠狠婷婷综合久久 | 国产精品亚洲专区无码不卡 | 乱中年女人伦av三区 | 欧美性黑人极品hd | 亚洲日韩乱码中文无码蜜桃臀网站 | 国色天香社区在线视频 | 国产精品福利视频导航 | 丰满人妻精品国产99aⅴ | 国产莉萝无码av在线播放 | 亚洲日韩av片在线观看 | 精品欧洲av无码一区二区三区 | 99久久无码一区人妻 | 狠狠色噜噜狠狠狠狠7777米奇 | 极品尤物被啪到呻吟喷水 | 奇米影视7777久久精品人人爽 | 国产精品人人妻人人爽 | 日本精品少妇一区二区三区 | 中文字幕无码av波多野吉衣 | 人妻少妇被猛烈进入中文字幕 | 欧美丰满熟妇xxxx性ppx人交 | 国产成人一区二区三区别 | 两性色午夜免费视频 | 无码人妻丰满熟妇区毛片18 | 动漫av网站免费观看 | 人妻与老人中文字幕 | 国产精品国产自线拍免费软件 | 夫妻免费无码v看片 | 成人无码视频在线观看网站 | 日韩亚洲欧美精品综合 | 一本久道久久综合狠狠爱 | 玩弄中年熟妇正在播放 | 国产97人人超碰caoprom | 国产xxx69麻豆国语对白 | 美女极度色诱视频国产 | 日本一本二本三区免费 | 欧美日韩一区二区免费视频 | 在线看片无码永久免费视频 | 久久精品一区二区三区四区 | 日本熟妇乱子伦xxxx | 无码人妻精品一区二区三区不卡 | 日本熟妇大屁股人妻 | 欧美激情一区二区三区成人 | 亚洲另类伦春色综合小说 | 国产精品无码一区二区三区不卡 | 动漫av一区二区在线观看 | 国产九九九九九九九a片 | 亚洲欧美日韩综合久久久 | 牲欲强的熟妇农村老妇女视频 | 亚洲熟女一区二区三区 | 欧美一区二区三区视频在线观看 | 国产免费无码一区二区视频 | 性生交大片免费看女人按摩摩 | 精品国偷自产在线 | 国产电影无码午夜在线播放 | 好男人www社区 | 精品人妻人人做人人爽 | 国精品人妻无码一区二区三区蜜柚 | 成人无码视频免费播放 | 亚洲国产日韩a在线播放 | 欧美野外疯狂做受xxxx高潮 | 色一情一乱一伦一区二区三欧美 | 乌克兰少妇xxxx做受 | 免费网站看v片在线18禁无码 | 中文字幕乱码亚洲无线三区 | 久久精品国产一区二区三区肥胖 | 久久亚洲精品中文字幕无男同 | 久久zyz资源站无码中文动漫 | 激情内射亚州一区二区三区爱妻 | 亚洲爆乳精品无码一区二区三区 | 成年女人永久免费看片 | 亚洲成av人在线观看网址 | 国产av无码专区亚洲a∨毛片 | 国产精品第一区揄拍无码 | 国产高潮视频在线观看 | 亚洲成av人在线观看网址 | 欧美喷潮久久久xxxxx | 欧美三级a做爰在线观看 | 亚洲日韩av片在线观看 | 婷婷六月久久综合丁香 | 1000部啪啪未满十八勿入下载 | 老熟女重囗味hdxx69 | 一本精品99久久精品77 | 麻豆精品国产精华精华液好用吗 | 欧美成人高清在线播放 | 学生妹亚洲一区二区 | 人妻体内射精一区二区三四 | 东京热一精品无码av | 国产口爆吞精在线视频 | 女人被男人爽到呻吟的视频 | 久久综合给久久狠狠97色 | 99久久婷婷国产综合精品青草免费 | 亚洲精品一区二区三区大桥未久 | 精品 日韩 国产 欧美 视频 | 国产成人一区二区三区别 | 天下第一社区视频www日本 | 正在播放老肥熟妇露脸 | 亚洲中文字幕无码中文字在线 | 国产亚洲精品久久久久久 | 一本久久伊人热热精品中文字幕 | 久久久久人妻一区精品色欧美 | 国产国产精品人在线视 | 7777奇米四色成人眼影 | 中文无码精品a∨在线观看不卡 | 亚洲国产欧美在线成人 | 国产亚洲精品精品国产亚洲综合 | 国产av剧情md精品麻豆 | 欧美怡红院免费全部视频 | www国产亚洲精品久久久日本 | 日本精品少妇一区二区三区 | 成熟人妻av无码专区 | 色窝窝无码一区二区三区色欲 | 亚洲综合无码一区二区三区 | 国产精品亚洲专区无码不卡 | 特大黑人娇小亚洲女 | 人妻有码中文字幕在线 | 亚洲欧洲中文日韩av乱码 | 精品成人av一区二区三区 | 国产真人无遮挡作爱免费视频 | 午夜熟女插插xx免费视频 | 牲欲强的熟妇农村老妇女 | aⅴ亚洲 日韩 色 图网站 播放 | 国产一区二区不卡老阿姨 | 亚洲色成人中文字幕网站 | 国产精品亚洲一区二区三区喷水 | 国产精品理论片在线观看 | 色综合久久久久综合一本到桃花网 | 久久99国产综合精品 | 国产免费无码一区二区视频 | av香港经典三级级 在线 | 无码国产激情在线观看 | 麻豆精产国品 | 成人免费视频视频在线观看 免费 | 18精品久久久无码午夜福利 | 久久五月精品中文字幕 | 精品亚洲成av人在线观看 | 亚欧洲精品在线视频免费观看 | 久久亚洲精品中文字幕无男同 | 亚洲日韩中文字幕在线播放 | 亚洲成a人片在线观看无码 | 中文久久乱码一区二区 | 性啪啪chinese东北女人 | 少妇高潮喷潮久久久影院 | 色欲人妻aaaaaaa无码 | 午夜福利一区二区三区在线观看 | 天堂久久天堂av色综合 | 国产综合在线观看 | 波多野结衣aⅴ在线 | 亚洲男人av天堂午夜在 | 国产一区二区三区四区五区加勒比 | 中文无码精品a∨在线观看不卡 | 国产欧美熟妇另类久久久 | 免费人成在线观看网站 | 国产精品久久国产三级国 | 日日天干夜夜狠狠爱 | 扒开双腿吃奶呻吟做受视频 | 丰满人妻被黑人猛烈进入 | 乱人伦人妻中文字幕无码 | 久久人人97超碰a片精品 | 欧美日韩色另类综合 | 大乳丰满人妻中文字幕日本 | 性开放的女人aaa片 | 久久久www成人免费毛片 | 老熟妇仑乱视频一区二区 | 丝袜美腿亚洲一区二区 | 大色综合色综合网站 | 亚洲国产欧美国产综合一区 | 色综合视频一区二区三区 | 老司机亚洲精品影院 | 亚洲国产日韩a在线播放 | 精品国产成人一区二区三区 | 纯爱无遮挡h肉动漫在线播放 | 在线成人www免费观看视频 | 久久综合香蕉国产蜜臀av | 亚洲成在人网站无码天堂 | 国产一区二区三区日韩精品 | 人人妻人人澡人人爽欧美一区九九 | 一二三四在线观看免费视频 | 亚洲国产日韩a在线播放 | 亚洲狠狠婷婷综合久久 | 欧美日韩一区二区免费视频 | 亚洲色偷偷偷综合网 | 免费无码av一区二区 | 国产亚洲美女精品久久久2020 | 亚洲天堂2017无码 | 沈阳熟女露脸对白视频 | 免费网站看v片在线18禁无码 | 欧美丰满老熟妇xxxxx性 | 久久综合网欧美色妞网 | 国产人妻精品一区二区三区不卡 | 久久熟妇人妻午夜寂寞影院 | 四虎4hu永久免费 | 漂亮人妻洗澡被公强 日日躁 | 2020久久香蕉国产线看观看 | 日本爽爽爽爽爽爽在线观看免 | 国产精品va在线观看无码 | 国产日产欧产精品精品app | 97久久国产亚洲精品超碰热 | 黑人巨大精品欧美一区二区 | 啦啦啦www在线观看免费视频 | 国内精品一区二区三区不卡 | 午夜熟女插插xx免费视频 | 四虎国产精品一区二区 | 亚洲a无码综合a国产av中文 | 青草青草久热国产精品 | 亚洲国产精品毛片av不卡在线 | 秋霞特色aa大片 | 久久久国产一区二区三区 | 青青久在线视频免费观看 | 亚洲中文字幕乱码av波多ji | 性欧美熟妇videofreesex | 国产亚洲人成a在线v网站 | 日本一卡二卡不卡视频查询 | 色婷婷综合中文久久一本 | 精品久久久无码中文字幕 | 国产亚洲欧美在线专区 | 黑人玩弄人妻中文在线 | 人妻少妇精品久久 | 亚洲中文字幕无码中字 | 国産精品久久久久久久 | 日韩欧美成人免费观看 | 四十如虎的丰满熟妇啪啪 | 奇米影视7777久久精品 | 国产精品嫩草久久久久 | 久久久亚洲欧洲日产国码αv | 亚洲一区二区三区含羞草 | 国产后入清纯学生妹 | 日本精品人妻无码免费大全 | 精品少妇爆乳无码av无码专区 | 国产成人无码av在线影院 | 精品少妇爆乳无码av无码专区 | 国产精品对白交换视频 | 丰满人妻精品国产99aⅴ | 中文字幕无码热在线视频 | 无人区乱码一区二区三区 | 亚洲一区二区三区播放 | 成人一在线视频日韩国产 | 久久久久99精品成人片 | 亚洲 a v无 码免 费 成 人 a v | 欧美熟妇另类久久久久久多毛 | 亚洲精品成人av在线 | 国产热a欧美热a在线视频 | 四虎影视成人永久免费观看视频 | 九一九色国产 | 2020久久香蕉国产线看观看 | 国产偷自视频区视频 | 成人影院yy111111在线观看 | 国产亚洲精品久久久ai换 | 国产性生交xxxxx无码 | 激情亚洲一区国产精品 | 男女爱爱好爽视频免费看 | 欧美放荡的少妇 | 日本一区二区更新不卡 | 牲交欧美兽交欧美 | 无码av岛国片在线播放 | 久久99精品国产麻豆蜜芽 | 2020久久香蕉国产线看观看 | 永久免费精品精品永久-夜色 | 成人精品视频一区二区 | 久久久久久av无码免费看大片 | 任你躁国产自任一区二区三区 | 九月婷婷人人澡人人添人人爽 | 国产亚洲欧美在线专区 | 日日夜夜撸啊撸 | 日韩人妻少妇一区二区三区 | 亚洲呦女专区 | 在线a亚洲视频播放在线观看 | 国内揄拍国内精品人妻 | 一本色道久久综合狠狠躁 | 漂亮人妻洗澡被公强 日日躁 | 精品国产精品久久一区免费式 | 精品国精品国产自在久国产87 | 日本一本二本三区免费 | 精品亚洲韩国一区二区三区 | 日韩精品无码一本二本三本色 | 久久无码中文字幕免费影院蜜桃 | 国内少妇偷人精品视频免费 | 中文亚洲成a人片在线观看 | 免费观看又污又黄的网站 | 少妇厨房愉情理9仑片视频 | 精品国偷自产在线 | 欧洲熟妇精品视频 | 97精品人妻一区二区三区香蕉 | 狠狠综合久久久久综合网 | www国产精品内射老师 | 日韩av无码中文无码电影 | 欧美成人高清在线播放 | 免费中文字幕日韩欧美 | 久久精品99久久香蕉国产色戒 | 亲嘴扒胸摸屁股激烈网站 | 无码吃奶揉捏奶头高潮视频 | 国产内射爽爽大片视频社区在线 | 成 人影片 免费观看 | 亚洲国产欧美国产综合一区 | 中文字幕无码日韩欧毛 | 一本无码人妻在中文字幕免费 | 国产又爽又猛又粗的视频a片 | 免费国产成人高清在线观看网站 | 夜夜夜高潮夜夜爽夜夜爰爰 | 天天躁夜夜躁狠狠是什么心态 | 欧美亚洲日韩国产人成在线播放 | 精品一二三区久久aaa片 | 久久久国产一区二区三区 | 中文字幕人妻丝袜二区 | 在线精品国产一区二区三区 | 人人爽人人澡人人人妻 | 99久久精品国产一区二区蜜芽 | 在线播放无码字幕亚洲 | 国产无遮挡又黄又爽免费视频 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 亚洲男人av天堂午夜在 | 欧美日韩亚洲国产精品 | 国产无av码在线观看 | 精品国产精品久久一区免费式 | 久久精品国产99精品亚洲 | 亚洲色在线无码国产精品不卡 | 亚洲色无码一区二区三区 | 性做久久久久久久久 | 国产成人午夜福利在线播放 | 国产精品99久久精品爆乳 | 真人与拘做受免费视频一 | 无码国产激情在线观看 | 国产精品久久久久影院嫩草 | 对白脏话肉麻粗话av | 男女作爱免费网站 | 免费人成在线观看网站 | а√天堂www在线天堂小说 | 国内少妇偷人精品视频免费 | 中文无码精品a∨在线观看不卡 | 精品无码国产一区二区三区av | 婷婷五月综合缴情在线视频 | 亚洲一区二区三区国产精华液 | 国产xxx69麻豆国语对白 | 久久久久亚洲精品男人的天堂 | 亚洲中文无码av永久不收费 | 国产真人无遮挡作爱免费视频 | 精品一区二区三区波多野结衣 | 国产明星裸体无码xxxx视频 | 青青青手机频在线观看 | 大肉大捧一进一出视频出来呀 | 青青草原综合久久大伊人精品 | 久久国语露脸国产精品电影 | 午夜精品一区二区三区在线观看 | 麻豆蜜桃av蜜臀av色欲av | 亚洲人亚洲人成电影网站色 | 国产色xx群视频射精 | 无码精品国产va在线观看dvd | 在线精品国产一区二区三区 | 国产超级va在线观看视频 | 国产莉萝无码av在线播放 | 乱人伦人妻中文字幕无码久久网 | 少妇被黑人到高潮喷出白浆 | 婷婷六月久久综合丁香 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 在线播放免费人成毛片乱码 | 在线看片无码永久免费视频 | 久久这里只有精品视频9 | 国产午夜无码视频在线观看 | 人妻aⅴ无码一区二区三区 | 亚洲欧美中文字幕5发布 | 久久久婷婷五月亚洲97号色 | 无码纯肉视频在线观看 | 波多野结衣高清一区二区三区 | 久久精品中文闷骚内射 | 蜜桃臀无码内射一区二区三区 | 伊人久久大香线蕉亚洲 | 蜜桃视频插满18在线观看 | 国产又爽又猛又粗的视频a片 | 久久久久久亚洲精品a片成人 | 日本熟妇人妻xxxxx人hd | 精品久久久中文字幕人妻 | 国产成人无码av一区二区 | 97无码免费人妻超级碰碰夜夜 | 精品无人国产偷自产在线 | 亚洲区欧美区综合区自拍区 | 少妇性荡欲午夜性开放视频剧场 | 欧美日韩在线亚洲综合国产人 | 中文字幕乱码中文乱码51精品 | 亚洲国产高清在线观看视频 | 成人性做爰aaa片免费看不忠 | 性欧美videos高清精品 | 欧美黑人巨大xxxxx | 精品国精品国产自在久国产87 | 亚洲欧洲日本综合aⅴ在线 | 成人精品天堂一区二区三区 | 少妇太爽了在线观看 | 中文精品无码中文字幕无码专区 | 偷窥村妇洗澡毛毛多 | 欧美日韩人成综合在线播放 | 图片小说视频一区二区 | 日本精品少妇一区二区三区 | 国产在线精品一区二区三区直播 | 日本护士毛茸茸高潮 | 熟女体下毛毛黑森林 | 欧美zoozzooz性欧美 | 撕开奶罩揉吮奶头视频 | 欧美阿v高清资源不卡在线播放 | 熟妇人妻激情偷爽文 | 正在播放东北夫妻内射 | 狂野欧美性猛xxxx乱大交 | 日韩欧美群交p片內射中文 | 一本一道久久综合久久 | 国产精品视频免费播放 | 中文字幕av日韩精品一区二区 | 国产农村乱对白刺激视频 | 国产精品沙发午睡系列 | 日本www一道久久久免费榴莲 | 亚洲精品综合一区二区三区在线 | 乱人伦人妻中文字幕无码久久网 | 精品无码一区二区三区的天堂 | 精品国产精品久久一区免费式 | 亚洲 另类 在线 欧美 制服 | 午夜不卡av免费 一本久久a久久精品vr综合 | 秋霞成人午夜鲁丝一区二区三区 | 精品久久久久久人妻无码中文字幕 | 在线成人www免费观看视频 | 国精品人妻无码一区二区三区蜜柚 | 暴力强奷在线播放无码 | 精品熟女少妇av免费观看 | 亚洲色欲色欲欲www在线 | 天堂无码人妻精品一区二区三区 | 日日碰狠狠丁香久燥 | 国产97在线 | 亚洲 | 中文字幕精品av一区二区五区 | 丰满少妇弄高潮了www | 伊人久久大香线蕉av一区二区 | 性欧美牲交xxxxx视频 | 欧美黑人性暴力猛交喷水 | 伊人色综合久久天天小片 | 动漫av网站免费观看 | 在线看片无码永久免费视频 | 亚洲中文字幕无码一久久区 | 对白脏话肉麻粗话av | 欧美性生交xxxxx久久久 | 国产激情精品一区二区三区 | 午夜精品一区二区三区的区别 | 在教室伦流澡到高潮hnp视频 | 国产内射爽爽大片视频社区在线 | 亚洲精品久久久久久一区二区 | 99久久精品日本一区二区免费 | 国产午夜亚洲精品不卡下载 | 夜精品a片一区二区三区无码白浆 | 中国女人内谢69xxxxxa片 | 国产亚洲日韩欧美另类第八页 | 亚洲男人av香蕉爽爽爽爽 | 欧美成人家庭影院 | 特黄特色大片免费播放器图片 | 无码精品国产va在线观看dvd | 波多野结衣 黑人 | 综合网日日天干夜夜久久 | 日韩视频 中文字幕 视频一区 | 色综合久久中文娱乐网 | 在线观看国产一区二区三区 | 男人的天堂2018无码 | 日本熟妇大屁股人妻 | 国产亚洲人成在线播放 | 欧美日韩一区二区综合 | 亚洲一区av无码专区在线观看 | 天堂一区人妻无码 | 国产乱子伦视频在线播放 | 国产精品久久久久久久9999 | 中文字幕乱码中文乱码51精品 | 久久婷婷五月综合色国产香蕉 | 动漫av网站免费观看 | 内射欧美老妇wbb | 精品国产一区av天美传媒 | 亚洲va中文字幕无码久久不卡 | 日本饥渴人妻欲求不满 | 色一情一乱一伦 | 水蜜桃亚洲一二三四在线 | 亚洲色www成人永久网址 | 日韩av激情在线观看 | 牲交欧美兽交欧美 | 国产精品久久久午夜夜伦鲁鲁 | 久激情内射婷内射蜜桃人妖 | 300部国产真实乱 | 国产片av国语在线观看 | 精品国偷自产在线视频 | 日本精品人妻无码77777 天堂一区人妻无码 | 精品人妻中文字幕有码在线 | 老熟妇仑乱视频一区二区 | 午夜精品久久久久久久 | 久久久国产精品无码免费专区 | 亚洲毛片av日韩av无码 | 动漫av一区二区在线观看 | 国产人妻精品一区二区三区不卡 | 亚洲一区二区三区香蕉 | 免费国产成人高清在线观看网站 | 国产精品无码一区二区桃花视频 | 丰满人妻一区二区三区免费视频 | 久久久www成人免费毛片 | 97精品国产97久久久久久免费 | 99久久亚洲精品无码毛片 | 伊人久久大香线蕉午夜 | 亚洲最大成人网站 | 亚洲一区二区三区无码久久 | 国产精品久久国产三级国 | 亚洲无人区午夜福利码高清完整版 | 少妇一晚三次一区二区三区 | 日本va欧美va欧美va精品 | 国产真人无遮挡作爱免费视频 | 国产成人无码区免费内射一片色欲 | 国产精品自产拍在线观看 | 亚洲精品久久久久中文第一幕 | 欧美人与禽zoz0性伦交 | 久久久久成人片免费观看蜜芽 | 自拍偷自拍亚洲精品10p | 人人爽人人澡人人高潮 | 大胆欧美熟妇xx | 波多野结衣av一区二区全免费观看 | 国产亚洲精品久久久久久大师 | 午夜不卡av免费 一本久久a久久精品vr综合 | 99久久精品午夜一区二区 | 国产乱子伦视频在线播放 | 人人爽人人澡人人人妻 | 久久久婷婷五月亚洲97号色 | 久久国语露脸国产精品电影 | 成人动漫在线观看 | 在线观看欧美一区二区三区 | 亚洲经典千人经典日产 | 日本大乳高潮视频在线观看 | 国产精品99爱免费视频 | 午夜无码人妻av大片色欲 | 亚洲欧美国产精品专区久久 | 乱码av麻豆丝袜熟女系列 | 少妇无码av无码专区在线观看 | 国产无遮挡吃胸膜奶免费看 | 国产午夜亚洲精品不卡 | 亚洲综合在线一区二区三区 | 任你躁在线精品免费 | 亚洲日韩一区二区三区 | 波多野结衣av一区二区全免费观看 | 无码成人精品区在线观看 | 国产免费久久久久久无码 | 99久久婷婷国产综合精品青草免费 | 日本va欧美va欧美va精品 | 四虎国产精品一区二区 | 久久国产自偷自偷免费一区调 | 国产成人无码av一区二区 | 丰满肥臀大屁股熟妇激情视频 | 色综合天天综合狠狠爱 | 中文精品无码中文字幕无码专区 | 99久久精品午夜一区二区 | 亚洲乱码中文字幕在线 | 1000部啪啪未满十八勿入下载 | 黄网在线观看免费网站 | 日韩av无码一区二区三区不卡 | 97色伦图片97综合影院 | 午夜精品一区二区三区在线观看 | 久久国产精品精品国产色婷婷 | 狠狠色噜噜狠狠狠7777奇米 | 日本xxxx色视频在线观看免费 | 国产精品美女久久久网av | 国产午夜无码视频在线观看 | 极品尤物被啪到呻吟喷水 | 欧美老妇与禽交 | 无码中文字幕色专区 | 中文字幕无码免费久久99 | 牲欲强的熟妇农村老妇女视频 | 中文无码伦av中文字幕 | 在线观看国产午夜福利片 | 亚洲成色www久久网站 | 国产无av码在线观看 | 大肉大捧一进一出视频出来呀 | 精品久久久无码人妻字幂 | 久久久久久亚洲精品a片成人 | 精品乱码久久久久久久 | 天海翼激烈高潮到腰振不止 | 欧美日韩色另类综合 | 亚洲精品一区三区三区在线观看 | 成在人线av无码免观看麻豆 | 日日碰狠狠丁香久燥 | 99视频精品全部免费免费观看 | 成人欧美一区二区三区黑人 | 亚洲va中文字幕无码久久不卡 | 日韩人妻系列无码专区 | 夜先锋av资源网站 | 久久久av男人的天堂 | 国产97人人超碰caoprom | 中文久久乱码一区二区 | 国产精品-区区久久久狼 | 狠狠综合久久久久综合网 | 国产精品久久久久久久9999 | 人人超人人超碰超国产 | 性欧美videos高清精品 | 久久久久国色av免费观看性色 | 人妻中文无码久热丝袜 | 国产网红无码精品视频 | 国产精品久久久一区二区三区 | 亚洲无人区一区二区三区 | 国产69精品久久久久app下载 | 国产av人人夜夜澡人人爽麻豆 | 麻豆精品国产精华精华液好用吗 | 天天做天天爱天天爽综合网 | 久久99精品久久久久婷婷 | 我要看www免费看插插视频 | 成年女人永久免费看片 | 国产乱子伦视频在线播放 | 午夜福利试看120秒体验区 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 日韩av无码一区二区三区不卡 | 精品久久综合1区2区3区激情 | 日产国产精品亚洲系列 | a在线亚洲男人的天堂 | 最近中文2019字幕第二页 | 国产成人午夜福利在线播放 | 人妻少妇精品久久 | 在线观看国产午夜福利片 | 极品尤物被啪到呻吟喷水 | 樱花草在线播放免费中文 | 97夜夜澡人人爽人人喊中国片 | 鲁一鲁av2019在线 | 樱花草在线社区www | 国产精品第一国产精品 | 亚洲中文字幕av在天堂 | 少妇厨房愉情理9仑片视频 | 国产精品高潮呻吟av久久4虎 | 免费人成网站视频在线观看 | 中文字幕乱妇无码av在线 | 亚洲精品综合五月久久小说 | 午夜性刺激在线视频免费 | 18禁止看的免费污网站 | 亚洲精品中文字幕 | 一本无码人妻在中文字幕免费 | 成人毛片一区二区 | 成人精品天堂一区二区三区 | 性色av无码免费一区二区三区 | 奇米影视888欧美在线观看 | 秋霞特色aa大片 | 丰满少妇弄高潮了www | 最新国产麻豆aⅴ精品无码 | 国产精品久久久久久亚洲毛片 | 中文字幕av日韩精品一区二区 | 欧美丰满少妇xxxx性 | 日韩欧美中文字幕在线三区 | 久久精品国产精品国产精品污 | 国产人妻人伦精品1国产丝袜 | 国产亚洲精品精品国产亚洲综合 | 亚洲中文字幕av在天堂 | 色综合久久久无码网中文 | 国产精品毛片一区二区 | 最近免费中文字幕中文高清百度 | 亚洲熟妇色xxxxx欧美老妇 | 亚洲中文字幕无码中文字在线 | 一个人看的www免费视频在线观看 | 亚洲欧洲中文日韩av乱码 | 日本在线高清不卡免费播放 | 中文字幕日产无线码一区 | 国内揄拍国内精品少妇国语 | 日本精品人妻无码77777 天堂一区人妻无码 | 无套内谢的新婚少妇国语播放 | 国产午夜亚洲精品不卡 | 日本精品人妻无码77777 天堂一区人妻无码 | 成 人影片 免费观看 | 国产在线一区二区三区四区五区 | 亚洲国产精品一区二区美利坚 | 5858s亚洲色大成网站www | 无套内谢老熟女 | 欧美国产日产一区二区 | 中文无码伦av中文字幕 | 纯爱无遮挡h肉动漫在线播放 | 亚洲中文字幕va福利 | 国产精品沙发午睡系列 | 天天躁夜夜躁狠狠是什么心态 | 日本成熟视频免费视频 | v一区无码内射国产 | 精品国产一区av天美传媒 | 粗大的内捧猛烈进出视频 | 377p欧洲日本亚洲大胆 | 夜精品a片一区二区三区无码白浆 | 自拍偷自拍亚洲精品被多人伦好爽 | 亚洲天堂2017无码 | 国产精品多人p群无码 | 久久zyz资源站无码中文动漫 | 麻豆精产国品 | 人妻少妇精品无码专区二区 | 久久婷婷五月综合色国产香蕉 | 久久久婷婷五月亚洲97号色 | 国产成人精品久久亚洲高清不卡 | 无套内谢老熟女 | 欧美日韩亚洲国产精品 | 红桃av一区二区三区在线无码av | 久久人人爽人人爽人人片ⅴ | 亚洲 高清 成人 动漫 | 成年女人永久免费看片 | 理论片87福利理论电影 | 美女黄网站人色视频免费国产 | 亚洲 激情 小说 另类 欧美 | 人人澡人人妻人人爽人人蜜桃 | 三上悠亚人妻中文字幕在线 | 成人欧美一区二区三区 | 国产精品无码一区二区桃花视频 | 丰满少妇熟乱xxxxx视频 | 国产亲子乱弄免费视频 | 在线а√天堂中文官网 | 国产激情精品一区二区三区 | 综合网日日天干夜夜久久 | 少妇无套内谢久久久久 | 欧美丰满熟妇xxxx性ppx人交 | 国产又爽又黄又刺激的视频 | 亚洲国产高清在线观看视频 | 99久久精品国产一区二区蜜芽 | 国产肉丝袜在线观看 | 久久久精品人妻久久影视 | 国色天香社区在线视频 | 欧洲熟妇精品视频 | 日本又色又爽又黄的a片18禁 | 日日干夜夜干 | 纯爱无遮挡h肉动漫在线播放 | 久久久精品456亚洲影院 | 全黄性性激高免费视频 | 国产精品va在线播放 | 欧美一区二区三区视频在线观看 | 亚洲色www成人永久网址 | 中文字幕无码av波多野吉衣 | 少妇激情av一区二区 | 一本一道久久综合久久 | 国产精品久久精品三级 | 国产内射爽爽大片视频社区在线 | 国产一区二区三区精品视频 | 亚洲精品午夜国产va久久成人 | 日韩精品成人一区二区三区 | aⅴ亚洲 日韩 色 图网站 播放 | 欧美老妇交乱视频在线观看 | 国产人妻人伦精品1国产丝袜 | 中文无码伦av中文字幕 | 亚洲爆乳无码专区 | 九月婷婷人人澡人人添人人爽 | 久久午夜无码鲁丝片秋霞 | 精品无码成人片一区二区98 | 亚洲日韩乱码中文无码蜜桃臀网站 | 永久免费观看美女裸体的网站 | 国产亚洲人成在线播放 | 中文字幕日韩精品一区二区三区 | 久久久久久久人妻无码中文字幕爆 | 亚洲综合久久一区二区 | 又粗又大又硬毛片免费看 | 97久久精品无码一区二区 | 色狠狠av一区二区三区 | 97se亚洲精品一区 | 无码乱肉视频免费大全合集 | 2020最新国产自产精品 | 亚洲乱码日产精品bd | 日本欧美一区二区三区乱码 | 亚洲色大成网站www | 蜜臀av在线播放 久久综合激激的五月天 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 国产成人无码午夜视频在线观看 | www国产亚洲精品久久久日本 | 国产精品无码永久免费888 | 国产免费无码一区二区视频 | 一本色道久久综合亚洲精品不卡 | 久久99精品国产麻豆 | 国产另类ts人妖一区二区 | 一本色道久久综合亚洲精品不卡 | 青春草在线视频免费观看 | 帮老师解开蕾丝奶罩吸乳网站 | 亚洲中文无码av永久不收费 | 牲欲强的熟妇农村老妇女视频 | 中文字幕日产无线码一区 | 少妇被粗大的猛进出69影院 | 无码人妻av免费一区二区三区 | 成人免费视频视频在线观看 免费 | 成人精品天堂一区二区三区 | 精品国产乱码久久久久乱码 | 成人aaa片一区国产精品 | 欧美国产日韩亚洲中文 | 成人综合网亚洲伊人 | 成人三级无码视频在线观看 | 精品一区二区三区无码免费视频 | 国产97人人超碰caoprom | 四十如虎的丰满熟妇啪啪 | 午夜理论片yy44880影院 | 伊人久久大香线焦av综合影院 | 久久精品无码一区二区三区 | 无码任你躁久久久久久久 | 国产精品久久久久7777 | 国产黑色丝袜在线播放 | 久久伊人色av天堂九九小黄鸭 | 性欧美大战久久久久久久 | 好男人www社区 | 国产亚洲欧美日韩亚洲中文色 | 一本精品99久久精品77 | 国产一区二区三区四区五区加勒比 | 夜精品a片一区二区三区无码白浆 | 成人欧美一区二区三区黑人 | 精品日本一区二区三区在线观看 | 粗大的内捧猛烈进出视频 | 国产乱人伦av在线无码 | 少妇人妻偷人精品无码视频 | 丰满人妻精品国产99aⅴ | 成人无码精品一区二区三区 | 在线看片无码永久免费视频 | 大地资源网第二页免费观看 | 久久久久久久人妻无码中文字幕爆 | 日韩少妇内射免费播放 | 亚洲国产精品毛片av不卡在线 | 亚洲国产欧美国产综合一区 | 久久天天躁狠狠躁夜夜免费观看 | 国产精品人妻一区二区三区四 | 日韩人妻无码中文字幕视频 | 俺去俺来也www色官网 | 中国女人内谢69xxxxxa片 | 国内老熟妇对白xxxxhd | 国产口爆吞精在线视频 | 女人被爽到呻吟gif动态图视看 | 成人无码视频免费播放 | 国产色在线 | 国产 | 亚洲日本va中文字幕 | a片在线免费观看 | 国产激情综合五月久久 | 久久zyz资源站无码中文动漫 | 男女下面进入的视频免费午夜 | 十八禁真人啪啪免费网站 | 亚洲一区av无码专区在线观看 | 亚洲精品国产品国语在线观看 | 久久久久国色av免费观看性色 | 少妇高潮喷潮久久久影院 | 少妇性俱乐部纵欲狂欢电影 | 亚洲综合无码久久精品综合 | 少妇无套内谢久久久久 | 六十路熟妇乱子伦 | 亚洲熟妇色xxxxx欧美老妇y | 国产精品99久久精品爆乳 | 中文字幕久久久久人妻 | 亚洲日韩av片在线观看 | 夜夜夜高潮夜夜爽夜夜爰爰 | 国产精品久久国产精品99 | 天天燥日日燥 | 婷婷丁香五月天综合东京热 | 久久国产自偷自偷免费一区调 | 乱中年女人伦av三区 | 人人妻人人藻人人爽欧美一区 | 少女韩国电视剧在线观看完整 | 真人与拘做受免费视频一 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 综合人妻久久一区二区精品 | 国产成人精品无码播放 | 高潮喷水的毛片 | 日韩精品a片一区二区三区妖精 | 日韩人妻无码一区二区三区久久99 | 国色天香社区在线视频 | 亚洲综合色区中文字幕 | 国产乱子伦视频在线播放 | 欧洲美熟女乱又伦 | 精品欧美一区二区三区久久久 | 永久免费观看美女裸体的网站 | 国产成人无码a区在线观看视频app | 九九在线中文字幕无码 | 亚洲欧美日韩综合久久久 | 国产高清不卡无码视频 | 黑人大群体交免费视频 | 中文字幕人成乱码熟女app | 亚洲精品久久久久久久久久久 | 欧美人与禽zoz0性伦交 | 国产精品久免费的黄网站 | 国产福利视频一区二区 | 成人试看120秒体验区 | 亚洲中文字幕无码中文字在线 | 免费男性肉肉影院 | 成人女人看片免费视频放人 | 无码国模国产在线观看 | 久久99热只有频精品8 | 久久99精品久久久久久动态图 | 国产成人亚洲综合无码 | 丰满人妻精品国产99aⅴ | 精品国精品国产自在久国产87 | 国产九九九九九九九a片 | 精品成在人线av无码免费看 | 色诱久久久久综合网ywww | 久久精品视频在线看15 | 波多野结衣乳巨码无在线观看 | 久精品国产欧美亚洲色aⅴ大片 | 亚洲一区二区三区在线观看网站 | 一本久道高清无码视频 | 亚洲国产成人a精品不卡在线 | 日日摸夜夜摸狠狠摸婷婷 | 成年美女黄网站色大免费全看 | 欧美野外疯狂做受xxxx高潮 | 色综合久久中文娱乐网 | 精品亚洲韩国一区二区三区 | 无码人妻黑人中文字幕 | 久久99精品久久久久久 | 国产一区二区不卡老阿姨 | 风流少妇按摩来高潮 | 嫩b人妻精品一区二区三区 | 亚洲成a人片在线观看无码 | 超碰97人人做人人爱少妇 | 97se亚洲精品一区 | 免费人成网站视频在线观看 | 99久久亚洲精品无码毛片 | 亚洲gv猛男gv无码男同 | 美女黄网站人色视频免费国产 | 成人片黄网站色大片免费观看 | 精品一区二区不卡无码av | 欧美35页视频在线观看 | 国产精品久久久久9999小说 | 亚洲爆乳无码专区 | 一本久道久久综合狠狠爱 | 女人被男人爽到呻吟的视频 | 黑人巨大精品欧美一区二区 | 国产免费久久精品国产传媒 | 国产成人综合美国十次 | 色婷婷av一区二区三区之红樱桃 | 国产三级精品三级男人的天堂 | 兔费看少妇性l交大片免费 | 色婷婷久久一区二区三区麻豆 | 人妻少妇精品无码专区二区 | 欧美一区二区三区 | 任你躁国产自任一区二区三区 | 日本又色又爽又黄的a片18禁 | 玩弄人妻少妇500系列视频 | 国产午夜福利亚洲第一 | 久久久精品国产sm最大网站 | 欧美国产日产一区二区 | 丁香花在线影院观看在线播放 | √天堂资源地址中文在线 | 久久亚洲中文字幕精品一区 | 日本一卡2卡3卡四卡精品网站 | 国产亚洲精品久久久闺蜜 | 日韩av无码中文无码电影 | 国产精品资源一区二区 | 久久综合给合久久狠狠狠97色 | 日韩人妻少妇一区二区三区 | 亚洲无人区午夜福利码高清完整版 | 日韩精品一区二区av在线 | 欧美35页视频在线观看 | av无码久久久久不卡免费网站 | 少妇性俱乐部纵欲狂欢电影 | 中文亚洲成a人片在线观看 | 波多野结衣一区二区三区av免费 | 熟妇激情内射com | 色综合久久88色综合天天 | 亚洲一区二区三区在线观看网站 | 欧美日本精品一区二区三区 | 一个人免费观看的www视频 | 亚洲中文字幕在线观看 | 国产免费久久精品国产传媒 | 99在线 | 亚洲 | 亚洲日本在线电影 | 丰满肥臀大屁股熟妇激情视频 | 精品无码一区二区三区的天堂 | 人妻少妇精品久久 | 国产一区二区不卡老阿姨 | 亚洲国产欧美在线成人 | 亚洲 另类 在线 欧美 制服 | 亚洲中文字幕无码中字 | 强伦人妻一区二区三区视频18 | 日本xxxx色视频在线观看免费 | 夜夜夜高潮夜夜爽夜夜爰爰 | 精品无码国产自产拍在线观看蜜 | 亚无码乱人伦一区二区 | 午夜不卡av免费 一本久久a久久精品vr综合 | 波多野结衣乳巨码无在线观看 | 丰腴饱满的极品熟妇 | 亚洲精品国产第一综合99久久 | 亚洲狠狠色丁香婷婷综合 | 大屁股大乳丰满人妻 | 无码国产乱人伦偷精品视频 | 精品国偷自产在线视频 | √8天堂资源地址中文在线 | 国产乱人伦偷精品视频 | 日韩人妻系列无码专区 | 天堂无码人妻精品一区二区三区 | 老太婆性杂交欧美肥老太 | 国产精品对白交换视频 | 亚洲成av人片天堂网无码】 | 麻豆国产97在线 | 欧洲 | 亚洲熟妇色xxxxx亚洲 | www国产精品内射老师 | 国产做国产爱免费视频 | 无码人妻丰满熟妇区五十路百度 | 久久精品国产一区二区三区 | 成在人线av无码免费 | 偷窥村妇洗澡毛毛多 | 76少妇精品导航 | 国精产品一区二区三区 | 少妇性俱乐部纵欲狂欢电影 | 激情人妻另类人妻伦 | 亚洲国产精品无码久久久久高潮 | 性欧美熟妇videofreesex | 青春草在线视频免费观看 | 亚洲の无码国产の无码影院 | 亚洲成在人网站无码天堂 | 国产精品对白交换视频 | 超碰97人人射妻 | 日韩精品久久久肉伦网站 |