I’ve recently started a project in Delphi XE8 that has me using Delphi a lot more again. It’s designed to be cross-platform, however for now the GUI side uses VCL controls, so the initial iteration is going to be restricted to Windows. I’ll be re-using the back end however, so FireDAC was chosen for database connectivity. I also chose to use LiveBindings to ease the rest into “cross-platformess”, and so that I could bind the data to just about any controls I want.

So far the design is fairly simple: I have a few TListView components that are displaying some data; one of which is the detail in a classic master-detail arrangement. I’m inserting detail records in code in the back end by passing the ID of the master record, and the ID of another source being used as a lookup, viz:

[sourcecode language=”delphi”] function TdmMain.AddListItem(ListID, TitleID: Integer): Integer;
begin
  qryItems.Append;
  qryItemsListID.Value := ListID;
  qryItemsTitleID.Value := TitleID;
  qryItemsSeq.Value := 0; //
  qryItemsActive.Value := 1;
  qryItems.Post;
  Result := qryItemsID.Value;
end;[/sourcecode]

I figured this would be pretty straightforward, however when I called this method I’d receive the error:

Dataset not in edit or insert mode

On the line just after the append. I figured there must be some kind of side-effect that was causing the dataset to change from dsInsert state to dsBrowse. Debugging that kind of situation can be real messy, as the code traces deep into the VCL (I still need to figure out how to avoid tracing into the System unit).

I figured if it was due to notifications to observers that the dataset had changed, wrapping the call thus:

[sourcecode language=”delphi”] function TdmMain.AddListItem(ListID, TitleID: Integer): Integer;
begin
qryItems.DisableControls;
try
  qryItems.Append;
  qryItemsListID.Value := ListID;
  qryItemsTitleID.Value := TitleID;
  qryItemsSeq.Value := 0; //
  qryItemsActive.Value := 1;
  qryItems.Post;
finally
qryItems.EnableControls;
end;
  Result := qryItemsID.Value;
end;[/sourcecode]

Would alleviate the immediate problem, which it does, however it introduces another: the list control isn’t updated with the new data.

The next step was to Google for whether anyone else is having the same problem. Nine times out of ten, this would provide me an answer. Either this was the other one, or I just wasn’t able to find the right search terms, so I posted a message to Embarcadero’s developer forums. I seriously wasn’t expecting an answer for a few days, however Marco Cantu answered very promptly, followed by Dmitry Arefiev, both Embarcadero employees, the latter being their FireDAC expert (unsurprising, since he is the original author).

Dmitry’s solution was right on the money. There’s an issue with the GetRecNo method of TFDDataset, in the FireDAC.Comp.Dataset unit. Here’s where it pays to have an edition of Delphi that has source:

In order to fix the issue, I made a copy of the unit (I prefer to leave the original source intact), put it somewhere in the project path, and made the following change:

[sourcecode language=”delphi”] function TFDDataset.GetRecNo: Integer;
begin
  if IsEmpty or (State = dsInsert) then
Result := -1
else
Result := GetRowIndex() + FUnidirRecsPurged + 1;
end;[/sourcecode]

I also copied the FireDAC.inc unit into the same location, because the compiler needs to find that file in order to successfully compile.

Problem solved! I created this post in case someone else comes across this issue.