I like programming in Delphi, but I don’t particularly like writing applications that work with database backends, but sometimes I just can’t avoid it (have been working on one for several weeks now, and it wasn’t too bad really). But one thing that can drive me nuts is when you have got an error, want to investigate the cause and it’s a bloody nuisance to try and get the value of the current record in a dataset. So, finally I wrote this little helper unit which I want to share with you.
All it does is export one public function TDataset_Dump which is meant to be called from the debugger’s “Evaluate and Modify” window:
TDataset_Dump(SomeDataset)
If called like this, it will magically open a text file in the associated application and display the content of the current record of the dataset you passed to it.
Alternatively you can let it dump the whole dataset like this:
TDataset_Dump(SomeDataset, -1)
Or all remaining records in the dataset (until EOF) like this:
TDataset_Dump(SomeDataset, 0)
Or you can pass it a number of records it should dump:
TDataset_Dump(SomeDataset, 5)
For this to work, you need to add the unit u_dzDatasetDump to the unit with the dataset you want to debug, otherwise the debugger won’t know about the function.
The unit is part of my dzlib library. It needs a few units from it (I’m a lazy bastard ™, so since I use the library in most of my programs I didn’t see the point of trying to avoid dependencies, but it should’t be too difficult to remove them.).
OK, so here goes the unit:
///<summary> /// Add this unit only for debug purposes. /// It exports TDataset_Dump for dumping a dataset from the evaluate and modify dialog /// of the Delphi debugger. </summary> unit u_dzDatasetDump; interface uses SysUtils, Classes, DB; ///<summary> /// @param Count: 1 -> Dump the current record only (default) /// -1 -> Dump the whole dataset /// 0 -> Dump from the current record until EOF /// >1 -> Dump a maximum of n records starting from the current /// @returns the file name the data was written to </summary> function TDataset_Dump(_ds: TDataset; _Count: Integer = 1): string; implementation uses u_dzLineBuilder, u_dzVariantUtils, u_dzFileUtils, u_dzExecutor, u_dzOsUtils; const DUMP_ALL = -1; procedure TDataset_DumpHeaders(_ds: TDataset; _sl: TStrings); var lb: TLineBuilder; i: Integer; fld: TField; begin lb := TLineBuilder.Create; try for i := 0 to _ds.FieldCount - 1 do begin fld := _ds.Fields[i]; lb.Add(fld.FieldName); end; _sl.Add(lb.Content) finally FreeAndNil(lb); end; end; procedure TDataset_DumpCurrent(_ds: TDataset; _sl: TStrings); var lb: TLineBuilder; i: Integer; fld: TField; begin lb := TLineBuilder.Create; try for i := 0 to _ds.FieldCount - 1 do begin fld := _ds.Fields[i]; lb.Add(Var2Str(fld.Value)); end; _sl.Add(lb.Content) finally FreeAndNil(lb); end; end; procedure TDataset_DumpToEof(_ds: TDataset; _sl: TStrings); begin while not _ds.Eof do begin TDataset_DumpCurrent(_ds, _sl); _ds.Next; end; end; procedure TDataset_DumpAll(_ds: TDataset; _sl: TStrings); begin _ds.First; TDataset_DumpToEof(_ds, _sl); end; function TDataset_Dump(_ds: TDataset; _Count: Integer = 1): string; var sl: TStringList; bm: Pointer; begin Result := ''; sl := TStringList.Create; try if not Assigned(_ds) then begin sl.Add('<dataset not assigned>'); if _Count = -1 then begin // do nothing this is a dummy call to fool the linker Exit; //==> end; end else if not _ds.Active then begin sl.Add('<dataset not active>'); end else if _ds.IsEmpty then begin sl.Add('<dataset is empty>'); end else begin bm := nil; _ds.DisableControls; try bm := _ds.GetBookmark; TDataset_DumpHeaders(_ds, sl); if _Count = DUMP_ALL then begin TDataset_DumpAll(_ds, sl); end else begin if _ds.Eof then begin sl.Add('<dataset is at eof>'); end else if _Count = 0 then begin TDataset_DumpToEof(_ds, sl); end else begin while (not _ds.Eof) and (_Count > 0) do begin TDataset_DumpCurrent(_ds, sl); _ds.Next; Dec(_Count); end; end; end; finally _ds.GotoBookmark(bm); _ds.EnableControls; end; end; if Assigned(_ds) then Result := _ds.Name; if Result = '' then Result := 'NONAME'; Result := itpd(TFileSystem.GetTempPath) + 'dump_of_' + Result + '.txt'; sl.SaveToFile(Result); OpenFileWithAssociatedApp(Result, True); finally FreeAndNil(sl); end; end; initialization // Make sure the linker doesn't eliminate the function TDataset_Dump(nil, -1); end.