1. 程式人生 > >delphi中 dataset容易出錯的地方

delphi中 dataset容易出錯的地方

 最近寫delphi專案,用到的資料集中的dataset,一直修改exception啊,寫下過程。

 

 在對資料集進行任何操作之前,首先要開啟資料集。要開啟資料集,可以把Active屬性設為True,例如:
  CustTable.Active := True;
  也可以呼叫Open函式,例如:CustQuery.Open;
  要關閉資料集,可以把Active屬性設為False或者呼叫Close函式。

其次 如果有exception的話,就要判斷state,下面我列舉下state的值(這樣是我從網上找的)

State屬性是隻讀的,下面列出了State屬性可能的值:
.dsInactive 資料集已關閉,不能訪問它的資料;
.dsBrowse 資料集已開啟,可以瀏覽資料但不能修改資料;
.dsEdit 此時為編輯狀態,可以修改資料;
.dsInsert 此時可以插入一條新的記錄;
.dsSetKey 只適用於TTable和TClientDataSet,此時可以設定範圍和鍵值,並且可以呼叫GotoKey函式;
.dsCalcFields 正在處理OnCalcFields事件(當欄位需要指定一個值的時候促發的事件),此時不能修改非計算欄位的值;
.dsCurValue 內部使用;
.dsNewValue 內部使用;
.dsOldValue 內部使用;
.dsFilter 正在進行過濾操作。 

當我看到我的專案的state為 dsInactive ,被踢我有多高行啦,然後立馬改為dsedit,尼瑪。但是還不管用,然後又看到方法:

如果應用程式要修改資料集的資料,必須首先進入dsEdit狀態。要進入dsEdit狀態,可以呼叫Edit。不過,呼叫Edit並不能保證一定能進入dsEdit狀態,這還取決於CanModify屬性的值。如果這個屬性返回True的話,表示資料集是可以讀和寫的。

終於完成了,尼瑪,就因為這被經理說,效率慢,該錯誤還是比較容易的,但是exception,就不是那麼簡單。下面補充下dataset的其他state的介紹:

下面的功能我是沒用到的::

 

 

6.7.2 插入新的記錄
  要在資料集中插入新的記錄,首先要進入dsInsert狀態。要進入dsInsert狀態,可以呼叫Insert或Append函式。不過,呼叫Insert或Append不一定會使資料集進入dsInsert狀態,還取決於CanModify屬性的值。
  一旦進入了dsInsert狀態,使用者就可以在資料控制元件(一般是TDBGrid)中插入一條新的記錄,並給這條記錄輸入資料。
  如果要通過程式設計來插入新的記錄,就要注意Insert和Append的區別。Insert將把一條新的記錄插入到當前記錄的前面,而Append將把一條新的記錄新增到資料集的末尾。
  插入了新的記錄後,應當呼叫Post或在CachedUpdates屬性設為True的情況下呼叫ApplyUpdates把新的記錄寫到資料集中。
  如果資料集是已建立了索引的Paradox或dBASE表,新記錄將自動移到恰當的位置。
  如果資料集沒有建立索引,新記錄就插入到資料集的當前位置(Insert)或末尾(Append)。
6.7.3 刪除記錄
  呼叫Delete函式將刪除當前記錄,並且使資料集回到dsBrowse狀態。如果窗體上有TDBNavigator構件的話,使用者可以單擊導航器上的“Delete”按鈕刪除當前記錄。當前記錄被刪除後,下一條記錄就成為當前記錄。
  如果刪除的本來就是最後一條記錄,則前一條記錄成為當前記錄。
6.7.4 修改整條記錄
  除了TDBGrid和TDBNavigator外,大部分資料控制元件只能工作於資料集的一個或幾個欄位,而不是整條記錄。
  不過,TDataSet提供了若干個方法可以直接修改整條記錄而不是單獨的欄位,這些方法包括:
.AppendRecord類似於Append,但可以給欄位賦值,不需要呼叫Post;
.InsertRecord類似於Insert,但可以給欄位賦值,不需要呼叫Post;
.SetFields對當前記錄的欄位賦值,需要顯式地呼叫Post。
  上述三個方法都要傳遞一個TVarRec型別的陣列作為引數,該陣列的每一個元素對應著一個欄位的值。如果陣列的元素個數小於資料集的欄位個數,剩下欄位的值就是NULL。
  對於沒有建立索引的資料集來說,AppendRecord把一條新的記錄加到資料集的末尾。對於已建立索引的資料集來說,新記錄將自動移到一個恰當的位置。
  SetFields用於對當前記錄的欄位賦值。在呼叫SetFields之前,首先要呼叫Edit,使資料集進入dsEdit狀態。呼叫了SetFields後,需要顯式地呼叫Post函式。
  呼叫SetFields時,如果您只想對部分欄位賦值,讓其他欄位的值保持不變,可以用NULL或NIL去賦值。
  假設一個數據集中有五個欄位,分別是Name、Capital、Continent、Area和Population,可以這樣對它們賦值:
CountryTable.InsertRecord([Japan, Tokyo, Asia]);
  上述程式在資料集中插入了一條新的記錄,並且對前三個欄位賦了值。現在可以再次對當前記錄賦值,不過,這次只想對Area欄位和Population欄位賦值,程式就要這樣寫:
With CountryTable Do
Begin
If Locate(Name, Japan, loCaseInsensitive) then
Begin
Edit;
SetFields(NIL, NIL, NIL, 344567, 164700000);
Post;
End;
End;
  注意:此處要用NIL而不是NULL,否則,前三個欄位會被設為空。
6.8 事 件
  TDataSet的事件主要分為兩大類,一類是Before系列,另一類是After系列,列表如下:
.BeforeOpen,AfterOpen發生在資料集開啟前後;
.BeforeClose,AfterClose發生在資料集關閉前後;
.BeforeInsert,AfterInsert發生在插入了一條新的記錄前後;
.BeforeEdit,AfterEdit 發生在進入dsEdit狀態前後;
.BeforePost,AfterPost 發生在寫資料集的前後;
.BeforeCancel,AfterCancel發生在取消修改的前後;
.BeforeDelete,AfterDelete發生在刪除記錄的前後。
  此外,當資料集中增加了一條新的記錄時就會觸發OnNewRecord事件,當“計算欄位”的值需要重算時將觸發OnCalcFields事件。
  Before系列的事件常常用來中止操作。例如,當呼叫Delete函式試圖刪除當前記錄時,在當前記錄將要刪除前會觸發BeforeDelete事件,可以在處理BeforeDelete事件的控制代碼中呼叫Abort或觸發一個異常放棄刪除當前記錄,程式示例如下:
Pocedure TForm1.TableBeforeDelete (Dataset: TDataset)
Begin
If MessageDlg(Delete This Record?, mtConfirmation, mbYesNoCancel, 0) <> mrYes Then Abort;
End;
  After系列的事件往往用來在狀態列上通知使用者,程式示例如下:
Procedure TForm1.Table1AfterDelete(DataSet: TDataSet);
Begin
StatusBar1.SimpleText := Format(有%d 條記錄,[DataSet.RecordCount]);
End;
  OnCalcFields事件主要用於給出“計算欄位”的值。AutoCalcFields屬性的值決定了什麼時候會發生OnCalcFields事件。
  如果AutoCalcFields屬性設為True,下列情況下會發生OnCalcFields事件:
.資料集被開啟時;
.在資料控制元件中,輸入焦點從一條記錄移到另一條記錄;
.在資料控制元件中,輸入焦點從一個欄位移到另一個欄位;
.當前記錄被修改或從資料庫中檢索了一條記錄。
  不過,即使AutoCalcFields屬性設為False,當資料集中的任意一個非計算欄位的值發生變化時都會觸發OnCalcFields事件。
  由於OnCalcFields事件有可能是頻繁發生的,因此,處理OnCalcFields 事件的程式碼要儘可能地簡短。在AutoCalcFields屬性設為True的情況下,在處理OnCalcFields事件的控制代碼中不能修改資料集的資料,因為一旦當前記錄被修改,又要觸發OnCalcFields事件,從而導致無限迴圈。例如,假設您在處理OnCalcFields事件的控制代碼中呼叫了Post,就會觸發OnCalcFields事件,導致再次呼叫Post,再次觸發OnCalcFields事件……
6.9 TBDEDataSet
  TBDEDataSet是從TDataSet繼承下來的,它提供了通過BDE(BorlandDatabase Engine)訪問資料的能力。這一節主要介紹TBDEDataSet,讀者應當對前面介紹的TDataSet已經有了比較深刻的認識。
  與TDataSet一樣,TBDEDataSet也是虛擬的和抽象的,除非您想建立自定義的資料集,否則,一般不需要直接用到TBDEDataSet。
  TBDEDataSet過載了TDataSet中涉及記錄導航、索引和書籤的方法,增加了一些處理BLOB欄位、快取更新的屬性、方法和事件。
6.9.1 CacheBlobs屬性
  TBDEDataSet的CacheBlobs屬性用於控制BDE是否把BLOB欄位的內容放到快取中。如果這個屬性設為True,當應用程式讀取BLOB欄位的值時,BDE將把BLOB欄位的內容放在快取中,這樣,當應用程式下次要讀取這個欄位的值時,就不必再從資料庫伺服器那兒去檢索,只要直接從記憶體中取過來就行了,這樣可以提高應用程式的效能。
  不過,如果應用程式需要頻繁地更新BLOB欄位的值,這時候反而應當把CacheBlobs屬性設為False,這樣能保證檢索到的BLOB欄位的值總是最新的。
6.9.2 快取更新
  TBDEDataSet提供了快取更新的技術。所謂快取更新就是,應用程式從資料庫中檢索資料,在本地快取中建立一個副本,使用者對資料進行修改後,也只是反映在快取中,以後可以呼叫ApplyUpdates一次性地把所有的修改反映到資料集中。
  可以看出,快取更新技術可以明顯地提高應用程式的效能,而且可以方便地取消修改,只要還沒有呼叫ApplyUpdates。下面列出了TBDEDataSet中有關快取更新的屬性、方法和事件:
.CachedUpdates如果這個屬性設為True,快取更新有效;
.UpdateObject用於指定一個TUpdateSQL構件來更新基於查詢的資料集;
.UpdatePending如果快取中有未決的記錄,這個屬性就返回True;
.UpdateRecordTypes指定資料集中哪些記錄是可見的;
.UpdateStatus返回當前的更新狀態;
.OnUpdateError如果更新過程中出錯將觸發這個事件;
.OnUpdateRecord每更新一條記錄就會觸發一次這個事件;
.ApplyUpdates把快取中的資料寫到資料集中;
.CancelUpdates把快取中未決的修改取消;
.CommitUpdates把快取清掉;l FetchAll從資料庫檢索所有記錄到快取中;
.RevertRecord撤消對當前記錄的修改。
6.10 TDBDataSet
  TDBDataSet是從TBDEDataSet繼承下來的,它提供了資料庫和會話期管理的能力。
  TDBDataSet中增加了若干個屬性和方法用於管理資料庫和BDE會話期,包括:
.CheckOpen檢查資料庫是否已開啟;
.Database返回一個TDatabase構件;
.DBHandle返回一個BDE控制代碼,呼叫BDE的API時要用到這個控制代碼;
.DBLocale返回當前的國際語言驅動程式;
.DBSession返回一個BDE會話期物件;
.DatabaseName用於指定要訪問的資料庫;
.SessionName用於指定一個BDE會話期物件。
  這裡詳細解釋一下DatabaseName屬性和SessionName屬性。如果應用程式要訪問遠端資料庫伺服器如Sybase、Oracle或InterBase,應當用TDatabase構件來連線資料庫,此時,應當設定DatabaseName屬性指定要連線的資料庫,可以設為TDatabase構件的名稱。如果沒有顯式地使用TDatabase構件,DatabaseName屬性應當設為BDE 別名。對於Paradox和dBASE表來說,可以設為表的路徑。
  SessionName屬性用於指定一個BDE會話期物件。如果應用程式沒有顯式地使用TSession構件,不必設定這個屬性。如果應用程式顯式地使用了多個TSession構件,應當設定SessionName屬性指定其中一個。
  一般來說,應用程式用不到DBHandle、DBLocale和DBSession等屬性,除非要直接呼叫BDE的API。這三個屬性都是隻讀的。
  TDBDataSet中還有一個只讀的Provider屬性,它能夠返回一個IProvider介面。在多層的Client/Server應用程式中,客戶程式需要通過IProvider介面與應用伺服器通訊。