01. marts 2001 - 12:00Der er
11 kommentarer og 2 løsninger
Databasekomponent i unit for sig?
Hejsa :)
Jeg har flere skærmbilleder og units der skal tilgå en database. Derfor vil jeg gerne lave én unit der sørger for databasekomponenten (TIBDatabase). Jeg har oprettet en ny unit, men hvordan benytter jeg de komponenter fra komponentmenúen når der ikke er en tilhørende form jeg kan \'trække\' dem ned på?
Skal jeg selv til at kreere objekterne eller er der en eller anden smart metode?
Brug (som også martinlind forslår) et Data-module. Jeg har således lavet et gennerelt DataModul som jeg bruger i alle mine projekter. Dette tager sig af at logge på vores InterBase server (og AS/400). Ydermere giver det dig den fordel (i forhold til at placere en TIBDatabase på hver form) at der kun laves en forbindelse til serveren og der bruges kun tid på at åbne/lukkke denne når programmet startes/stoppes (ved at bruge TDataModule\'ts OnCreate/OnDestroy.
Ligeledes kan du i dette datamodul placere DB-relaterede funktioner som går på tværs af dine projekter: Bruger-logon, Låsning af records osv.
pellelil>> Lyder spændende ;))) Er den noget du kan undvære en kopi af?? Vil meget gerne se det. Eller endnu bedre hvis du har et mindre projekt hvor jeg kan se hvorledes du opbygger det hele. Er første projekt jeg laver der skal køre mod en database, og er lidt i tvivl om hvordan jeg skal strukturere det smart...
Hvis du vil, er min mailadresse peter@aude.dk Og der kompenseres selvfølgelig med flere points ;)
Jeg har desværre et et \"mindre projekt\" der egner sig så (i første omgang) må du nøjes med nedenstående.
Lav et nyt projekt. Dernæst vælger du File->\"New->New->Data Module\" (ikke at forveksle med \"File->New->Data Modules->Customer Data\". I dette DataModule indsætter du en TIBDatabase og en TIBTransaction (som du \"kobler sammen\"). Jeg holder mine Database-connection (og transaktion) lukket indtil programmet starter hvorefter jeg lader Data Modulets \"OnCreate\" åbne denne. Ligeledes når programmet afsluttes så sikre jeg mig at der ikke er nogle aktive transaktioner og jeg sørger for at database connection\'en igen lukkes (i DataModulet \"OnDestroy\"). Dette kunne se ud som flg:
<SNIP> procedure TDataModuleFccInterBase.DataModuleCreate(Sender: TObject); begin FormWait := TFormWait.Create(Nil); FormWait.Show; FormWait.ProgressBar1.Position := 0; FormWait.ProgressBar1.Max := 2; FormWait.Update; //--- Logon to InterBase server --- if IB_FCCInterBase <> nil then begin if IB_FCCInterBase.Connected then IB_FCCInterBase.Connected := False; with IB_FCCInterBase do begin Params.Values[\'user_name\'] := \'IBUSERNAME\'; Params.Values[\'password\'] := \'IBPASSWORD\'; LoginPrompt := False; Connected := True; end; end; if IBT_FCCInterBase <> nil then begin if IBT_FCCInterBase.Active = False then IBT_FCCInterBase.Active := True; end; FormWait.ProgressBar1.Position := FormWait.ProgressBar1.Position + 1; FormWait.ProgressBar1.Update; //--- Logon to AS/400 --- if DB_EGDTA <> nil then begin if DB_EGDTA.Connected then DB_EGDTA.Connected := False; with DB_EGDTA do begin Params.Values[\'USER NAME\'] := \'AS400USERNAME\'; Params.Values[\'PASSWORD\'] := \'AS400PASSWORD\'; LoginPrompt := False; Connected := True; end; end; FormWait.ProgressBar1.Position := FormWait.ProgressBar1.Position + 1; FormWait.ProgressBar1.Update; FormWait.Close; end;
procedure TDataModuleFccInterBase.DataModuleDestroy(Sender: TObject); begin //--- LogOff InterBase server --- if (IBT_FCCInterBase <> Nil) and (IBT_FCCInterBase.InTransaction) then IBT_FCCInterBase.Commit; if (IB_FCCInterBase <> Nil) and (IB_FCCInterBase.Connected) then IB_FCCInterBase.Connected := False; //--- LogOff AS/400 --- if (DB_EGDTA <> Nil) and (DB_EGDTA.Connected) then DB_EGDTA.Connected := False; end; </SNIP>
Ovenstående logger dels på og af vores InterBase og AS/400. Desuden har jeg en FormWait som fortæller brugeren at de skal \"klappe hesten\" indtil der er logget på både InterBase og AS/400. Denne FormWait indeholder også en ProgressBar, som opdateres.
I de forms hvor du har brug for at access dine database, kan du bare include dit DataModule. Når du på disse forms indsætter TIBDataSet (og lign.) så sætter du bare disse\'s \"DataBase\" til at pege på den TIBDataBase der er på dit DataModule (i mit tilfælde: \"DataModuleFccInterBase.IB_FCCInterBase\").
Ligeledes hvis du eksempelvis har nogle TIBDataSets du ofte anvender så kan du placere disse på dit DataModule også da du således kan anvende disse. Jeg har eksempelvis på mit DataModule en TIBDataSet (med tilhørende TIBTransaction) som jeg anvender sammen med min bruger-logon funktion (jeg har således på IB lavet min egen brugerdatabase - da programmerne logger på vha. et \"standard IB brugernavn/password\"
<SNIP> Function _LogOnUser(UserIdStr, DecryptedPassword: String; Silent:Boolean): Boolean; begin Result := False; UserIdStr := AnsiUpperCase(UserIdStr); if (UserIdStr = \'\') then begin if not(Silent) then MessageDlg(\'You haven\'\'t entered a username\', mtError, [mbOK], 0); Exit; end; with DataModuleFccInterBase do begin if not trUserLogon.InTransaction then trUserLogon.StartTransaction; dsetUserLogon.Params.ByName(\'USERID_APP\').AsString := UserIdStr; if not dsetUserLogon.Active then dsetUserLogon.Open; try dsetUserLogon.First; if not dsetUserLogon.EOF then begin dsetUserLogon.Edit; if dsetUserLogon.FieldByName(\'ACCOUNT_DISABLED\').AsInteger = 1 then begin dsetUserLogon.FieldByName(\'FAILED_LOGON\').AsInteger := dsetUserLogon.FieldByName(\'FAILED_LOGON\').AsInteger + 1; if not(Silent) then MessageDlg(\'Your user-profile is currently disabled. You need to contact the \'+#13+#10+ \'administrator in order to have your user-profile enabled again.\', mtError, [mbOK], 0); end else begin if _DecryptPassword(dsetUserLogon.FieldByName(\'PWD\').AsString) = DecryptedPassword then begin dsetUserLogon.FieldByName(\'LAST_LOGON\').AsDateTime := Now; dsetUserLogon.FieldByName(\'FAILED_LOGON\').AsInteger := 0; with _GlobalUserLogedOn do begin UserIdStr := dsetUserLogon.FieldByName(\'USERID_APP\').AsString; UserIdInt := dsetUserLogon.FieldByName(\'USERID_DB\').AsInteger; FName := dsetUserLogon.FieldByName(\'NAME\').AsString; FPwd := dsetUserLogon.FieldByName(\'PWD\').AsString; FPwdExpired := (dsetUserLogon.FieldByName(\'PWD_EXPIRED\').AsInteger = 1); FPwdDate := dsetUserLogon.FieldByName(\'PWD_DATE\').AsDateTime; FUserLevel := dsetUserLogon.FieldByName(\'USERLEVEL\').AsInteger; FNetworkID := dsetUserLogon.FieldByName(\'NETWORKID\').AsString; FAS400ID := dsetUserLogon.FieldByName(\'AS400ID\').AsString; FEMail := dsetUserLogon.FieldByName(\'EMAIL\').AsString; FMailAlias := dsetUserLogon.FieldByName(\'MAILALIAS\').AsString; end; Result := True; end else begin dsetUserLogon.FieldByName(\'FAILED_LOGON\').AsInteger := dsetUserLogon.FieldByName(\'FAILED_LOGON\').AsInteger + 1; if dsetUserLogon.FieldByName(\'FAILED_LOGON\').AsInteger >= _MaxFailedLogon then begin dsetUserLogon.FieldByName(\'ACCOUNT_DISABLED\').AsInteger := 1; if not(Silent) then MessageDlg(\'You have entered a wrong password too many times, \'+#13+#10+ \'so your user-profile has been disabled. \'+#13+#10+\'\'+#13+#10+ \'You need to contact the administrator in order to have\'+#13+#10+ \'you user-profile enabled again.\', mtError, [mbOK], 0); end else begin if not(Silent) then MessageDlg(\'You have entered a wrong username/password.\', mtError, [mbOK], 0); end; end; end; dsetUserLogon.Post; end else begin if not(Silent) then MessageDlg(\'You have entered a wrong username/password.\', mtError, [mbOK], 0); end; finally if trUserLogon.InTransaction then trUserLogon.Commit; if dsetUserLogon.Active then dsetUserLogon.Close; end; end; end; </SNIP>
Med nye transaktioner undervejs mener jeg, laver man en ny TIBtransaction til hver query, eller bruger man den ene Transaction man oprettede i starten til alle querys og datasets?
Håber ikke jeg spørger for dumt, men er først liiige ved at finde ud af det her ;)
Hov, nu er jeg vist lidt for hurtig til at spørge i stedet for selv at undersøge!
Fra Delphi hjælpen: Note: In MIDAS applications, every query must be in its own transaction. You must use one transaction component for each query component.
flere Transaction\'s comp. er kun nødvendig vis du har flere instanser af dit datamodule og det har du kun hvis du kører multitier eller ISAPI, hvis det er en alm. app du laver så er det ikke nødvendigt med mere en en trans. comp.
Men hvis jeg i programmets start sætter transaction.active := true får jeg en fejl når jeg laver en transaction.StartTransaction fra en anden form. (transaction is active)
Er der nogen grund til at sætte den aktiv i starten?
I InterBase foregår ALT i transaktioner og det SKAL foregå i transaktioner. Dette betydder således at når du COMMIT\'er din transaktion så lukkes de Datasets der benytter denne transaktion. Ligeledes når flere Datasets deler samme transaktion så har du ikke (på samme måde) kontrol over de enkelte transaktioner. Den Transaktion der således er på DataModulet er knyttet til min TDataBase (da den kræver en sådan) men på hver af mine forms knytter jeg en separat Transaktion til hver Dataset.
Dog kan jeg sagtens i forbindelse med en Master/Detail finde på at \"dele\" samme transaktion til flere Dataset da ændringer i såvels Master som Detail kan \"fortrydes\" med et kald til Rollback (eller RollbackRetaining)
Hvis du gør som marinlind foreslår så er det nok at starte din (ene) transaktion når du starter dit program. Dog skal du være opmærksom på at du i dine forms SKAL kalde CommitRetaining og/eller RollBack retaining.
Hvis du i stedet gør som jeg foreslår (principielt en transaktion pr DataSet) så kan du groftsagt sige at den Transaktion der er på din DataBase ikke anvendes. På mine enkelte forms åbner jeg DataSet og starter transaktioner i formens OnCreate.
jeg ved da godt hvorn du skal gøre..men det koster*GGG*
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.