#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);
(whichuses 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.