MIDAS 3:更新多個資料表的方法

問題描述

  使用 Delphi5 + BDE + MIDAS
  我希望能一次更新兩個以上的資料表,可是這些異動必須在同一個交易
  中,也就是其中任何一個資料表發生錯誤時必須全部rollback,應該怎
  麼做?

典型的場景

  假設有一個訂單管理程式,當新增一筆訂單時需要同時更新客戶資料與
  訂單資料。一般的做法是在 DataSetProvider 的 BeforeUpdateRecord 
  事件中加入更新其他資料表的程式碼,例如:
  
  procedure TForm1.dspOrdersBeforeUpdateRecord(Sender: TObject;
    SourceDS: TDataSet; DeltaDS: TClientDataSet; UpdateKind: TUpdateKind;
  var Applied: Boolean);
  begin
    if UpdateKind = ukModify then
    begin
      qryUpdate.SQL.Text := 'update Customer set City="AAA" where CustNo="1221"';
      qryUpdate.ExecSQL;
    end;
  end;
  
  以上面的例子來看,會先更新客戶資料 Customer,然後再更新訂單資
  料 Orders。可是如果更新 Orders 時發生了錯誤,之前對於 Customer
  所做的異動卻無法 rollback 了。即使這兩個資料集元件都連結到相同
  的 TDatabase 元件也一樣。這樣一來仍然無法達到資料完整性。

解決方案 A:

  使用 Dan Miser 的 MIDAS Essensial Pack。使用時需 uses CDSUtil 
  單元,程式碼可以參考下面的範例:

    App server 端:

      procedure TMtsDmod.MyApplyUpdates(var vDeltaArray: OleVariant;
        vProviderArray: OleVariant);
      begin
        try
          CDSApplyUpdates(vDeltaArray, vProviderArray);
          SetComplete;
        except
          SetAbort;
        end;
      end;

    Client 端:

      procedure TForm1.btnApplyUpdatesClick(Sender: TObject);
      var
        cdsArray: array [0..1] of TCLientDataSet;
        vDeltaArray: OleVariant;
        vProviderArray: OleVariant;
      begin
        cdsArray[0] := cdsPubs;     // Master
        cdsArray[1] := cdsTitles;   // Detail
        vDeltaArray := RetrieveDeltas(cdsArray);
        vProviderArray := RetrieveProviders(cdsArray);
        DCOMConnection1.AppServer.ApplyUpdates(vDeltaArray, vProviderArray);
        ReconcileDeltas(cdsArray, vDeltaArray);
      end;


解決方案 B:
  
  不要讓 DataSetProvider 更新資料,讓其連接的資料集元件自行更新
  資料。首先,DataSetProvider 的 ResolveToDataSet 必須設為 True
  。然後在 qryOrders 的 OnUpdateRecord 事件中撰寫更新其他資料表
  的程式碼,像這樣:
  
  procedure TForm1.qryOrdersUpdateRecord(DataSet: TDataSet;
    UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
  begin
    if UpdateKind = ukModify then
    begin
      qryUpdate.SQL.Text := 'update Customer set City="AAA" where CustNo="1221"';
      qryUpdate.ExecSQL;
    end;
  end;
  
  你可以故意讓 Orders 在更新時發生錯誤,看看 Customer 資料表中 
  CustNo 為 '1221' 的那筆記錄的 City 欄位是否被改為 'AAA',如果
  不是的話就表示這個方法的確可行,你可以自行驗證看看。
    

解決方案 C:

  在 dataset provider 的 BeforeUpdateRecord 事件中自行處理掉所有
  的更新的工作。例如:

  procedure TForm1.dspOrdersBeforeUpdateRecord(Sender: TObject;
    SourceDS: TDataSet; DeltaDS: TClientDataSet; UpdateKind: TUpdateKind;
  var Applied: Boolean);
  begin
    qryCustomer.UpdateObject := usqlCustomer;
    usqlCustomer.SetParams(UpdateKind);
    usqlCustomer.ExecSQL(UpdateKind);

    qryOrders.UpdateObject := usqlOrders;
    usqlOrders.SetParams(UpdateKind);
    usqlOrders.ExecSQL(UpdateKind);

    Applied := True;
  end;

  使用這個方法時別忘了最後要將 Applied 設為 True, 告訴 dataset 
  provider 你已經完成更新資料的工作。

    Source: geocities.com/huanlin_tsai/faq

               ( geocities.com/huanlin_tsai)