Mega Code Archive

 
Categories / Delphi / VCL
 

Referential Integrity component for DBISAM (w Bug fixed)

Title: Referential Integrity component for DBISAM (w/ Bug fixed) Question: How to add referential integrity with cascade / update delete by using component ? Answer: { TCASCADE COMPONENT FOR DBISAM 1.21 PUBLIC DOMAIN RELEASE ON 26 APRIL 2000 BY JIRAYU WIRIYAPHIBOOL MODIFIED AND IMPROVED BY ...... FREELY USE OR MODIFY AT YOUR OWN RISK CONTACT ME AT : JIRAYU@SUNNCITY.COM VISIT MY WEBSITE WWW.SUNNCITY.COM FOR MANY FREE THINGS, INCLUDING JAVA ARCADE GAMES, ONLINE NEWS } { NEXT VERSION MULTI-THREADED CASCADE UPDATE & DELETE (NO WAITING FOR DETAILS TO CATCH UP) IF YOU SEE ANY FLAW OR UNSUITABLE CODE, PLEASE EMAIL ME ACCEPT ANY COMMENTS AND IMPROVEMENT ARE DEEPLY WELCOME } unit Cascade; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, DB, DBIsamTB; type TCascadeType = (ctUpdateAndDelete,ctUpdate,ctDelete,ctReference); TOnCascadeFail = Procedure (TableName:String;ErrorCode:Integer) of Object; TCascade = class(TComponent) private FRefMessage:String; FNormalBeforeEdit:TDataSetNotifyEvent; FNormalBeforeDelete:TDataSetNotifyEvent; FNormalBeforePost:TDataSetNotifyEvent; FNormalOnPostError:TDataSetErrorEvent; FNormalAfterPost:TDataSetNotifyEvent; FNormalAfterDelete:TDataSetNotifyEvent; FDBIsamDatabase:TDBIsamDatabase; FSubDataSet: TDBIsamTable; FMainDataSet: TDBIsamTable; FShadowSet:TDBIsamTable; FOnCascadeFail:TOnCascadeFail; FLinkField: String; FCascadeType: TCascadeType; procedure SetMainDataSet(const Value: TDBIsamTable); procedure SetSubDataSet(const Value: TDBIsamTable); procedure SetLinkField(const Value: String); protected Failed:Boolean; EditMode:Boolean; ByPass:Boolean; BeforeKey:String; AfterKey:String; Procedure FindMatchingField; Procedure KeepKey(DataSet:TDataSet); Procedure CascadeUpdate(DataSet:TDataSet); Procedure CascadeDelete(DataSet:TDataSet); Procedure CheckReference(DataSet:TDataSet); procedure CheckAllow(DataSet: TDataSet); Procedure CheckMasterError(DataSet: TDataSet; E: EDatabaseError; var Action: TDataAction); public Procedure Loaded;Override; Destructor Destroy;Override; procedure Notification(AComponent: TComponent; Operation: TOperation); Override; Constructor Create(AOwner:TComponent);Override; Procedure Update; published Property CascadeType:TCascadeType read FCascadeType write FCascadeType; Property LinkField:String read FLinkField write SetLinkField; Property MainDataSet:TDBIsamTable read FMainDataSet write SetMainDataSet; Property SubDataSet:TDBIsamTable read FSubDataSet write SetSubDataSet; Property OnCascadeFail:TOnCascadeFail read FOnCascadeFail write FOnCascadeFail; Property Database:TDBIsamDatabase read FDBIsamDatabase write FDBIsamDatabase; Property RefMessage:String read FRefMessage Write FRefMessage; end; procedure Register; implementation procedure Register; begin RegisterComponents('Amazing2', [TCascade]); end; { TCascade } procedure TCascade.Update; begin CascadeUpdate(FMainDataSet); end; procedure TCascade.CascadeUpdate(DataSet: TDataSet); begin If Assigned(FNormalAfterPost) then FNormalAfterPost(Dataset); AfterKey := FMainDataSet.FieldByName(FLinkfield).Value; If (BeforeKey AfterKey) and (EditMode) then Begin if (FCascadetype = ctUpdateAndDelete) or (FCascadetype = ctUpdate) then Begin While FShadowSet.Locate(FLinkField,BeforeKey,[]) do Begin FShadowSet.Edit; Case FShadowSet.FieldByName(FLinkField).DataType of ftInteger:FShadowSet.FieldByName(FLinkField).AsInteger := StrToInt(AfterKey); ftString:FShadowSet.FieldByName(FLinkField).AsString := AfterKey; End; FShadowSet.Post; End; End; FDBIsamDatabase.Commit; FSubDataSet.Refresh; If Not(FDBIsamDatabase.InTransaction) then FDBIsamDatabase.StartTransaction; End else FDBIsamDatabase.Commmit; end; destructor TCascade.Destroy; begin If Assigned(FNormalBeforeEdit) then FMainDataSet.BeforeEdit := FNormalBeforeEdit; If Assigned(FNormalAfterPost) then FMainDataSet.AfterPost := FNormalAfterPost; If Assigned(FNormalOnPostError) then FMainDataSet.OnPostError := FNormalOnPostError; If Assigned(FNormalBeforeDelete) then FMainDataSet.BeforeDelete := FNormalBeforeDelete; If Assigned(FNormalBeforePost) then FMainDataSet.BeforePost := FNormalBeforePost; inherited Destroy; end; procedure TCascade.KeepKey(DataSet: TDataSet); begin If Not(FDBIsamDatabase.InTransaction) then FDBIsamDatabase.StartTransaction; EditMode := True; If Assigned(FNormalBeforeEdit) then FNormalBeforeEdit(DataSet); BeforeKey := DataSet.FieldByName(FLinkField).Value; end; procedure TCascade.CheckAllow(DataSet: TDataSet); begin BeforeKey := DataSet.FieldByName(FLinkField).Value; If Assigned(FNormalBeforeDelete) then FNormalBeforeDelete(DataSet); If (FCascadeType = ctReference) then Begin If FSubDataSet.RecordCount 0 then Raise Exception.Create(fRefMessage); End; end; procedure TCascade.Loaded; begin inherited Loaded; If Not(CsDesigning in ComponentState) then Begin FNormalBeforeEdit := FMainDataSet.BeforeEdit; FNormalOnPostError := FMainDataSet.OnPostError; FNormalAfterPost := FMainDataSet.AfterPost; FNormalBeforeDelete := FMainDataSet.BeforeDelete; FNormalBeforePost := FMainDataset.BeforePost; FMainDataSet.BeforeEdit := KeepKey; FMainDataSet.BeforeDelete := CheckAllow; FMainDataSet.BeforePost := CheckReference; FMainDataSet.AfterPost := CascadeUpdate; FMainDataSet.AfterDelete := CascadeDelete; FMainDataSet.OnPostError := CheckMasterError; FShadowSet := TDBIsamTable.Create(Self); FShadowSet.DatabaseName := FSubDataSet.DatabaseName; FShadowSet.TableName := FSubDataSet.TableName; FShadowSet.Active := True; End; end; procedure TCascade.SetLinkField(const Value: String); begin FLinkField := Value; end; procedure TCascade.SetMainDataSet(const Value: TDBIsamTable); begin FMainDataSet := Value; FindMatchingField; end; procedure TCascade.SetSubDataSet(const Value: TDBIsamTable); begin FSubDataSet := Value; FindMatchingField; end; procedure TCascade.CheckMasterError(DataSet: TDataSet; E: EDatabaseError; var Action: TDataAction); begin FDBIsamDatabase.RollBack; If Not(FDBIsamDatabase.InTransaction) then FDBIsamDatabase.StartTransaction; If Assigned(FOnCascadeFail) then FOnCasCadeFail(FMainDataSet.TableName, EDBIsamEngineError(E).Errors[0].ErrorCode); Abort; end; procedure TCascade.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; If Operation = OpRemove then Begin If AComponent = FMainDataSet then FMainDataSet := Nil; If AComponent = FSubDataSet then FSubDataSet := Nil; If AComponent = FDBIsamDatabase then FDBIsamDatabase := Nil; End; end; procedure TCascade.CascadeDelete(DataSet: TDataSet); Var V:Variant; begin If Assigned(FNormalAfterDelete) then FNormalAfterDelete(Dataset); If (FCascadeType = ctUpdateAndDelete) or (FCascadeType = ctDelete) then Begin V := BeforeKey; While FShadowSet.Locate(LinkField,V,[]) do FShadowSet.Delete; FSubDataSet.Refresh; End; end; procedure TCascade.FindMatchingField; Var I,I2:Integer; begin If (FMainDataSet Nil) and (FSubDataSet Nil) and (Trim(FLinkField) = '') then Begin For I := 0 to FMainDataset.Fields.Count-1 do Begin For I2 := 0 to FSubDataSet.Fields.Count-1 do Begin If FMainDataSet.Fields[I].FieldName = FSubDataSet.Fields[I2].FieldName then FLinkField := FMainDataSet.Fields[I].FieldName; End; End; End; end; constructor TCascade.Create(AOwner: TComponent); begin inherited; FRefMessage := 'Has details can not change value or delete'; end; procedure TCascade.CheckReference(DataSet: TDataSet); begin If Assigned(FNormalBeforePost) then FNormalBeforePost(DataSet); If (FCascadeType = ctReference) then Begin If BeforeKey DataSet.FieldByName(FLinkField).Value then Begin Raise Exception.Create(FRefMessage); End; End; end; end.