Avatar billede skindbeni Nybegynder
29. juli 2008 - 23:48 Der er 9 kommentarer og
2 løsninger

Exception handler

Hej.

Jeg skal til at skrive en exception handler, hvilket er et nyt emne indenfor programmering for mig.

Min første exception handler skal bruges i forbindelse med en DBGrid og en Clientdataset, som er forbundet sammen. Clientdataset'et er ligeledes forbundet med en database, hvor feltet KONTO er primær nøgle.

Når en ny record postes i DBGrid'en / clientdataset'et får man en exception, hvis feltet KONTO ikke er udfyldt. Som standard er det en EDatabaseError med beskeden Field ’Konto’ must have a value. Den skal jeg have lavet "dansk"

Det som jeg vil gøre er, at jeg i clientdataset'ets BeforePost event skriver:

try
  clientdataset1.post;
except
  on E : EDatabaseError do
    showMessage('Feltet 'konto' skal udfyldes');
end

Dette efterlader nogle spørgsmål.

1. Er der en fejl? De medfører hos mig en fejl. Ikke en compilerfejl, men runtime, når jeg forsøger at indsætte en ny linie i min grid, skriver en tekst i et felt, men lader feltet konto være tomt for derefter at vælge en anden linie, så linien bliver postet. Jeg får en stack overflow error, hvis programmet køres fra Delphi og køres programmet via exe-filen lukker den uden yderligere info eller fejlbeskeder.

2. Er det rigtigt at placere exception handleren i BeforePost eventen?

3. Kan man ikke specificere eventhandleren mere præcist? En EDatabaseError kan jo være mange ting og er ikke ensbetydende med, at det skal være den samme fejlbesked, som brugeren skal have. Eksempelvis kan det jo være andre databasefejl end lige, at det er feltet Konto, der mangler at blive udfyldt.
Avatar billede pidgeot Nybegynder
30. juli 2008 - 00:12 #1
1+2) Jeg vil tro at idet du i din event handler kalder Post på dit dataset, bliver BeforePost kaldt (igen) - fordi den jo skal udføres INDEN post'en (hvilket også vil sige at posten allerede er i gang når du er nået til din Post-linje). Derfor får du en stack overflow - og programmet lukker ned fordi den ikke kan nå at reagere på det. Du bør derfor flytte det kode ud til der hvor du nu sætter din post igang (nu har jeg aldrig arbejdet med DBGrid og ClientDataSet, så der er du lidt på egen hånd).

3) Nej, du kan kun angive typen på den exception du vil fange - selvfølgelig kan du angive en anden type, men det skal jo være en der bliver returneret (måske er der en EDatasetFieldNotSet eller noget). Det kan du kombinere med at kigge på hvad der ligger i dit exception-objekt - igen, har ikke arbejdet med dem, og kender ikke dit direkte behov til fejlmeddelelsen, så kan ikke gøre andet end at pege dig i retningen af dens Message.
Avatar billede hrc Mester
30. juli 2008 - 09:15 #2
I BeforePost skal du validere data, "raise exceptions" og "abort post"

procedure ... (DataSet: TDataSet);
begin
  try
    with DataSet.FieldByName('konto') do
      if IsNull or (Value = 0) then
        raise Exception.Create('Feltet konto skal udfyldes');

  except
    on e: exception do
    begin
      ShowMessage(e.Message);
      abort;
    end;
  end;
end;
Avatar billede hrc Mester
30. juli 2008 - 09:17 #3
Du har ikke brug for at vente på EDatabaseErrors - medmindre du vil validere på DupKeys, altså hvis det kan se at man indsætter record med samme primærnøgle - hvilket kan ske i flerbrugermiljøer.
Avatar billede hrc Mester
30. juli 2008 - 09:20 #4
En EDatabaseError er i øvrigt direkte nedarvet fra Exception så der kan du ikke finde oplysninger. Nogle gange er EDatabaseError dog nedarvet til også at indeholde en errorcode som fortæller hvad der er gået galt:

  EUpdateError = class(EDatabaseError)
  private
    FErrorCode: Integer;
    FPreviousError: Integer;
    FContext: string;
    FOriginalException: Exception;
  public
    constructor Create(NativeError, Context: string;
      ErrCode, PrevError: Integer; E: Exception);
    destructor Destroy; override;
    property Context: string read FContext;
    property ErrorCode: Integer read FErrorCode;
    property PreviousError: Integer read FPreviousError;
    property OriginalException: Exception read FOriginalException;
  end;

Dukker sådan en fyr op kan du tjekke koden:

  try
    ..
  except
    on e: exception do
    begin
      if e is EUpdateError do
        case (e as EUpdateError).ErrorCode of
          ...
        end;
    end;
  end;
Avatar billede hrc Mester
30. juli 2008 - 09:22 #5
En skønhedsfejl:

  except
    on e: exception do
    begin
      ShowMessage(e.Message);
      abort;
    end;
  end;

skulle være:

  except
    on e: exception do
    begin
      abort;
      ShowMessage(e.Message);
    end;
  end;

Der er ingen grund til at have en "Post" ventende indtil brugeren trykker op. Det er især vigtigt hvis man bruger transaktioner.
Avatar billede skindbeni Nybegynder
30. juli 2008 - 13:38 #6
Hej. I må begge gerne smide et svar, jeg vil fordele pointene 75/25 til hrc/pidgeot

Hvad mener du i øvrigt med din sidste linie

Der er ingen grund til at have en "Post" ventende indtil brugeren trykker op. Det er især vigtigt hvis man bruger transaktioner

Er det ikke "...indtil brugeren trykker ok..." du mener eller har jeg misforstået koden?
Avatar billede pidgeot Nybegynder
30. juli 2008 - 14:40 #7
Værsgo :)
Avatar billede skindbeni Nybegynder
30. juli 2008 - 14:52 #8
@hrc

Jeg har prøvet din kode

procedure TfrmKontoplan.cdsKontoplanBeforePost(DataSet: TDataSet);
begin
  try
    with DataSet.FieldByName('Konto') do
      if IsNull or (Value = 0) then
        raise Exception.Create('Feltet konto skal udfyldes');
  except
    on E : Exception do
    begin
      abort;
      showmessage(e.Message);
    end;
  end;
end;

Men den medfører, at hvis fletet konto IKKE er indtastet, så kan jeg hverken poste, få fejlbesked, lukke eller noget. Ved ikke, om det er 'abort' der gør, at den "ikke gør noget"

Skal jeg have byttet rundt på noget eller mangler jeg noget?
Avatar billede hrc Mester
30. juli 2008 - 21:25 #9
Aborten er en usynlig exception der afbryder post-operationen. Din record kommer aldrig ud af dsInsert/dsEdit tilstanden så datasættet må du give et "Cancel" hvis du vil lukke uden at gemme.

I en form kan du eventuelt tage den operation i OnCloseQuery:

if Table1.State in [dsEdit,dsInsert] then
  if ModalResult = mrOK then
    Table1.Post
  else
    Table1.Cancel;
Avatar billede skindbeni Nybegynder
30. juli 2008 - 22:10 #10
@hrc: Husk et svar :o)
Avatar billede hrc Mester
31. juli 2008 - 11:04 #11
OK
Avatar billede Ny bruger Nybegynder

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.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester