ADOQuery AfterScroll не запускается с одной записью / без записи - PullRequest
0 голосов
/ 14 февраля 2019

У меня есть MasterQry и SlaveQry на форме.MasterQry выглядит примерно так:

select * from Header where Active = 1.

В этом событии AfterScroll у меня есть следующее:

select * from Slave where HeadID=:Header.ID

Теперь, если я сделаю:

if MasterQry.Active then MasterQry.Close;
MasterQry.Open;

, это работает безупречно, если у меня есть большезатем одна запись, но она не работает, если у меня есть только одна.

Даже если я делаю MasterQry.First;, ничего не происходит.

Если я пытаюсь MasterQry.AfterScroll(MasterQry), я получаю нарушение прав доступа.

Я выполнял рефакторинг своего кода и пытался сделать его более компактным, потому что я использовал много Open Open Locate ID (необходим для обновления данных, чтобы получить фактический статус, заблокирован ли он и т. Д.), И я сделал это:

function RefreshQuery(AQuery : TADOQuery; ID : integer) : boolean ; overload;
var AfterOpen,AfterScroll : TDataSetNotifyEvent;
begin
  result:=false;

  AfterOpen := AQuery.AfterOpen;
  AfterScroll := AQuery.AfterScroll;

  AQuery.AfterOpen:=nil;
  AQuery.AfterScroll:=nil;

  if AQuery.Active then AQuery.Close;
  AQuery.Open;

  if not AQuery.Locate('id', ID, []) then
    result:=false
  else
    result:=true;

  AQuery.AfterOpen:=AfterOpen;
  AQuery.AfterScroll:=AfterScroll;
  if Assigned(AQuery.AfterScroll) then
    AQuery.AfterScroll(AQuery);
end;

ОБРАТИТЕ ВНИМАНИЕ, этот код не универсален, но идеально подходит для моих нужд.Я заметил, что событие AfterScroll здесь запускается, даже если у меня есть только одна запись в MasterQry, или даже если у меня вообще нет записи.Я был действительно счастлив, я тестировал его несколько раз, и он дал правильные результаты.

Я проверил процедуры TADOQuery.First и TADOQuery.Locate, и у них был DoAfterScroll, но он не был запущен с одной записью или без записи.(SlaveQry был оставлен открытым в нежелательном состоянии)

Я много гуглил по этому поводу, но не смог найти причину.

Мой вопрос : почему это работает?почему AfterScroll срабатывает с одной или без записи.

Спасибо.

ОБНОВЛЕНИЕ

Я могу воспроизвести это только с помощью Microsoft SQL.Таким образом, чтобы проверить это, вам нужно.Две таблицы.

В MasterTable добавить две записи (ID, текст, активный)

1 Первая 1

2 Вторая 1

в SlaveTable добавить две или более записи (ID, HeadID, текст)

1,1, First-1

2,1, First-2

3,2, Second-1

4,2, Second-2

Теперь добавьте в форму один ADOConnection two ADOQueries .

в MainQuery у вас есть следующий текст

Select * from MasterTable where Active=1

в SlaveQuery у вас есть следующий текст

select * from SlaveTable where HeadID=:HeadID

на MainQuery.BeforeOpen у вас есть это:

MainQuery.AfterScroll:=nil;

на MainQuery.AfterScroll у вас есть это:

if SlaveQuery.Active then SlaveQuery.Close;
SlaveQuery.Parameters.ParamByName('HeadID').Value:=MainQueryID.Value;
SlaveQuery.Open;

в MainQuery.AfterOpen у вас есть это:

MainQuery.AfterScroll:=MainQueryAfterScroll;

Добавьте Button в эту форму:

Button1Click Событие содержит следующее:

if MasterQuery.Active then MasterQuery.Close;
MasterQuery.Open;

Таким образом, если вы теперь присоединяете сетку к обоим запросам, вы можете видеть, что она отлично работает.

Не закрывая программу, зайдите в диспетчер SQL Server и выполните следующую инструкцию обновления:

update MasterTable set Active=0

Нажмите кнопку Button1 в форме еще раз:

MasterQuery isEmtpy, SlaveQuery остался в последнем открытом состоянии.

Чтобы исправить это, вам нужно изменить Button1Click следующим образом:

var AfterOpen,AfterScroll : TDataSetNotifyEvent;
begin
  AfterOpen := AQuery.AfterOpen;
  AfterScroll := AQuery.AfterScroll;

  AQuery.AfterOpen:=nil;
  AQuery.AfterScroll:=nil;

  if AQuery.Active then AQuery.Close;
  AQuery.Open;

  AQuery.AfterOpen:=AfterOpen;
  AQuery.AfterScroll:=AfterScroll;
  if Assigned(AQuery.AfterScroll) then
    AQuery.AfterScroll(AQuery);
end;

И теперь это работает.Я не знаю почему, потому что MasterQuery.First должен вызывать DoAfterScroll, но ничего не происходит.Кажется, что установка AfterScroll на ноль, а затем обратно снова как-то запускает AfterScroll, даже если он имеет 1 запись или пуст.

1 Ответ

0 голосов
/ 14 февраля 2019

Как я сказал в комментарии, большая часть кода в вашем RefreshQuery не должна быть необходимой, так как связывание наборов данных Master-> Detail должно "просто работать".На самом деле ваш RefreshQuery вообще не нужен.

Я создал минимальный проект, основанный на ваших основных и подчиненных таблицах, просто удалив компоненты с поддона, подключив их и добавив ТОЛЬКО код вForm1.FormСоздать ниже.Содержимое ведомой сетки правильно отслеживает основную сетку, включая случай, когда нет соответствующих ведомых записей, то есть ведомая сетка отображается пустой.Обратите внимание, что нет никаких событий данных, которые необходимы, то есть нет вызовов AfterScroll и Locate и т. Д.

  type
    TForm1 = class(TForm)
      dsMaster: TDataSource;
      DBGrid1: TDBGrid;
      DBNavigator1: TDBNavigator;
      DBGrid2: TDBGrid;
      DataSource2: TDataSource;
      DBNavigator2: TDBNavigator;
      ADOConnection1: TADOConnection;
      qMaster: TADOQuery;
      qSlave: TADOQuery;
      qSlaveID: TIntegerField;
      qSlaveHeaderID: TIntegerField;
      qSlaveAText: TWideStringField;
      procedure FormCreate(Sender: TObject);
    public
    end;

  [...]

  procedure TForm1.FormCreate(Sender: TObject);
  begin
    qMaster.SQL.Text := 'select * from mastertable';

    qSlave.DataSource := dsMaster;
    qSlave.SQL.Text := 'select * from slavetable where headerid = :id';
    //  NOTE: because the DataSource property of qSlave is set to dsMaster,
    //  the ` = :id` tells the Ado run-time code to get the value of the
    //  ID field in the qMaster table.

    qMaster.Open;
    qSlave.Open;
  end;

Если вы хотите обновить таблицу Master или Slave в случае, если другой пользователь изменил записив нем вы можете сделать это следующим образом:

procedure TForm1.Button1Click(Sender: TObject);
begin
  qMaster.Refresh;
end;

, но помните, что таблица должна быть правильно настроена на сервере Sql.Пока поле идентификатора установлено в качестве первичного ключа и / или на нем установлен уникальный индекс на сервере, вызов Refresh должен работать нормально, но если нет, вы получите ошибку ссообщение с эффектом «Недостаточно ключевой информации для обновления или обновления».Конечно, вы можете выполнить обновление по таймеру (но не вызывайте его слишком часто, то есть чаще, чем раз в несколько секунд).

...