Исключение после поиска подробного TADOQuery

#oracle #delphi #ado #master-detail

#Oracle #delphi #ado #мастер-деталь

Вопрос:

У меня есть мастер и деталь TAdoQuery , называемые FMaster and FDetail . Они подключены через источник FMasterSource данных . Подробный запрос имеет параметр, который автоматически заполняется и обновляется в зависимости от основного запроса.

Теперь проблема в том, что после выполнения FDetail.Locate (или фактически FDetail.Recordset.Clone ) FMaster.Next генерируется исключение при попытке FDetail выполнить запрос с новым параметром:

   try
    FMaster.Open;
    FDetail.Open;
    FDetail.Locate('Id', 2, []);
    FMaster.Next; {<== throws Exception with ADO Errorcode 0x80040e05}
  finally
    FDetail.Close;
    FMaster.Close;
  end;
 

Согласно этому списку, Errorcode 0x80040e05 означает «Объект уже открыт».

  • Проблема, по-видимому, заключается в том, что FDetail.Locate создается клон базового набора записей Ado для последующего использования и сохраняется. Поэтому мы можем заменить Locate на recordsetClone := FDetail.Recordset.Clone(adLockReadOnly); (which uses Winapi.ADOInt ) и получить ту же ошибку.
  • Внутри FMaster.Next исключение генерируется при попытке FDetail запроса после установки нового параметра для MasterID. Это происходит в TCustomADODataSet.RefreshParams .
  • Когда вы отключаете деталь от Master, все работает просто отлично. Установка параметра FDetail вручную работает просто отлично.

Что я пропустил? Это ошибка в VCL? Или это еще одна странная ошибка ADO?

Исходный код

Я использую Oracle 11g XE и Delphi 10.4 Sydney.

Таблицы Oracle:

 drop table Detail;
drop table Master;

create table Master (
  Id NUMBER(2) not null, 
  constraint MasterPK primary key (Id)
);
create table Detail (
  Id       NUMBER(2) not null, 
  MasterId NUMBER(2), 
  constraint DetailPK primary key (Id),
  constraint DetailFK foreign key (MasterId) references Master(Id)
);
insert into Master values (1);
insert into Master values (2);
insert into Detail values (1,1);
insert into Detail values (2,1);
insert into Detail values (3,2);
 

Код Delphi:

 procedure TForm1.Button1Click(Sender: TObject);
begin
  //Initializing...
  FConnection.Provider := 'OraOLEDB.Oracle.1';
  FConnection.ConnectionString := 'Provider=OraOLEDB.Oracle.1;Password=xxxx;Persist Security Info=True;User ID=TEST;Data Source=XE';
  FConnection.LoginPrompt := False;
  FConnection.KeepConnection := False;

  FMaster.Connection := FConnection;
  FMaster.SQL.Text := 'select Id from Master';
  FMaster.CursorLocation := clUseServer;
  FMaster.CursorType := ctStatic;

  FMasterSource.DataSet := FMaster;

  FDetail.Connection := FConnection;
  FDetail.DataSource := FMasterSource;
  FDetail.SQL.Add('select Id, MasterId from Detail where MasterId = :Id');
  FDetail.Parameters[0].DataType := ftInteger;
  FDetail.CursorLocation := clUseServer;

  //Do stuff
  try
    FMaster.Open;
    FDetail.Open;
    //FDetail.Locate('Id', 2, []);
    recordsetClone := FDetail.Recordset.Clone(adLockReadOnly);
    FMaster.Next; {<== throws Exception with ADO Errorcode 0x80040e05}
  finally
    FDetail.Close;
    FMaster.Close;
  end;
end;
 

Комментарии:

1. Не уверен, поскольку обычно я бы реализовал это сам, не устанавливая основной набор данных, но я думаю, что вам не следует открывать подробный набор данных самостоятельно. Набор подробных данных должен быть (повторно) открыт путем открытия или прокрутки основного набора данных. Затем, вероятно, можно найти правильную запись в событии AfterOpen подробного набора данных.

2. Не ответ, но способ, которым вы используете try / finally для защиты Open с помощью Close, неверен. Вам нужно ДВЕ попытки / наконец. FDetail. Открытие может завершиться ошибкой и оставить FMaster открытым.

3. @fpiette Я пытался избежать двух блоков try / finally для удобства чтения. Вопрос исправлен. Если Close вызывается для набора данных, который не активен, ничего не происходит, поэтому он должен работать таким образом.

4. @GolezTrol Я только что попробовал: открытие FMaster не открывается автоматически FDetail . Active Наборы данных, не содержащие подробностей, похоже, игнорируются повсюду. Я не уверен, что сообщение об ошибке «Объект уже открыт» действительно много значит: у меня есть книга ADO, в которой говорится, что это сообщение также возникает, когда ADO пытается автоматически установить второе соединение, которое может не поддерживаться Oracle. (В настоящее время ADO делает это для вещей, которые невозможны только при одном соединении).

5. Возможно, ADO закрывается FDetail и по какой-то причине пытается открыть клон. У меня была другая ситуация, когда казалось, что ADO перепутал два набора записей, запрашивающих одну и ту же таблицу. К сожалению, я не могу воспроизвести это поведение atm.