Опубликован: 07.05.2010 | Уровень: специалист | Доступ: платный
Лекция 29:

Многоуровневая архитектура. Создание "тонкого" клиента

< Лекция 28 || Лекция 29: 12

Реализация подключений к серверным наборам данных

Вернемся к нашему проекту. Поместите в модуль данных два компонента ClientDataSet. Для подключения этих компонентов к наборам данных с сервера приложений, прежде всего, в свойстве RemoteServer нужно выбрать проводника, у нас это DCOMC. Далее в свойстве ProviderName следует выбрать нужного провайдера, который обеспечит связь с НД сервера. Для первого ClientDataSet это будет DSPLichData, для второго - DSPTelephones. Соответственно, у первого компонента ClientDataSet в свойстве Name впишите CDSLichData, у второго - CDSTelephones.

Для того, чтобы связать навигатор DBNavigator и сетки DBGrid на главной форме, нам потребуются два компонента DataSource с вкладки Data Access. Первый назовите dsLichData и в свойстве DataSet свяжите его с набором данных CDSLichData, второй назовите dsTelephones и свяжите его с CDSTelephones. Откройте оба ClientDataSet, переведя их свойство Active в True.

Вернитесь на главную форму. Навигатор и верхнюю сетку через свойство DataSource подключите к fDM.dsLichData, нижнюю сетку подключите к fDM.dsTelephones. Сетки при этом должны отобразить данные. Сохраните проект, скомпилируйте и загрузите. Сразу же при этом проявляется ошибка: нижняя сетка содержит подчиненные данные из таблицы Telephones, относящиеся к первой записи главной таблицы LichData. При смене записи в главной таблице, подчиненная таблица записей не меняет, несмотря на то, что мы организовали связь один-ко-многим на стороне сервера. Обусловлено это тем, что при начале работы подчиненная таблица получила нужные записи в локальный буфер, и теперь с ними работает, не изменяя его. Точно также, если мы сейчас попробуем сохранить данные таблиц в локальный файл, то записи главной таблицы сохранятся полностью, подчиненная же таблица сохранит лишь те записи, которые есть в кэше. Исправим эту ситуацию.

Вернитесь в модуль данных. Щелкните дважды по компоненту CDSLichData, который содержит данные главной таблицы. Откроется редактор полей. Щелкните по нему правой кнопкой, и командой AddFields (добавить поля) добавьте все имеющиеся там поля. Как вы можете заметить, помимо всех полей таблицы LichData редактор отобразил еще одно поле: TTelephones, которое имеет тип TDataSetField. Это специальное поле, которое содержит данные подчиненной таблицы, через него и будет создаваться нужная связь один-ко-многим. Кнопкой OK закройте редактор полей, добавив все имеющиеся там поля.

Перейдем к подчиненному компоненту CDSTelephones. В свойстве DataSetField выберите полученное поле главной таблицы CDSLichDataTTelephones. При этом автоматически очистятся свойства RemoteServer и ProviderName. Теперь этот НД связан с сервером через главную таблицу, а не напрямую. Снова сохраните проект, скомпилируйте и загрузите его. Теперь полный порядок: при изменении записи в главной таблице, изменяются записи подчиненной таблицы.

Однако это не все. На модуль данных установите также два диалога: SaveDialog и OpenDialog. Переименуйте их соответственно, в SD1 и OD1. Выделите оба диалога и откройте редактор свойства Filter. В редакторе укажите единственную строчку:

Файлы данных | *.dat

В свойстве DefaultExt диалогов укажите расширение по умолчанию, "dat" (разумеется, без кавычек). В свойстве FileName укажите имя файла по умолчанию: "LichData.dat". В свойстве InitialDir укажите папку по умолчанию: "C:\". Таким образом, мы настроили оба диалога на работу с файлом

C:\LichData.dat

Конечно, пользователь сможет изменить и адрес, и имя файла, но по умолчанию будут использованы наши настройки. Осталось изменить свойство Title диалогов. У OD1 укажите "Открыть файл с личными данными", у SD1 - "Сохранить файл с личными данными".

Вернемся к главной форме. Создайте обработчик нажатия на кнопку "Подключиться к базе". Код обработчика приведен ниже:

procedure TfMain.bConnectClick(Sender: TObject);
begin
  //если нет подключения:
  if not fDM.DCOMC.Connected then begin
    // подключаем DCOMConnected
    fDM.DCOMC.Connected:= True;
    //открываем наборы данных:
    fDM.CDSLichData.Open;
    fDM.CDSTelephones.Open;
  end; //if
end;

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

procedure TfMain.bRefreshClick(Sender: TObject);
begin
  //отменяем все неподтвержденные изменения:
  fDM.CDSLichData.CancelUpdates;
  fDM.CDSTelephones.CancelUpdates;
end;

При отмене неподтвержденных изменений с сервера считывается текущий набор данных, и сетки будут отображать действительное положение дел. Кнопка "Сохранить изменения в базу" вызовет следующий обработчик:

procedure TfMain.bSaveToDBClick(Sender: TObject);
var i,k: Integer; //для подсчета ошибок
begin
  i:= fDM.CDSLichData.ApplyUpdates(0);
  k:= fDM.CDSTelephones.ApplyUpdates(0);
  if i + k > 0 then
    ShowMessage('При сохранении данных выявлено ошибок: '+
       IntToStr(i+k))
  else ShowMessage('Данные сохранены');
end;

Переменные целого типа нам нужны для подсчета возможных ошибок при сохранении данных на сервер. Метод ApplyUpdates, вызванный для каждого НД, не только сохраняет данные на сервер, но и возвращает количество ошибок, если они были. Параметр 0 указывает, что сохранение данных не нужно прекращать, если при сохранении выявляются ошибки. Можете здесь указать 1. В этом случае сохранение прекратится сразу же, как будет выявлена какая либо ошибка. Если сумма переменных i и k окажется больше 0, ошибки были допущены. Иначе ошибок не было.

Код кнопки "Отключиться от базы" будет таким:

procedure TfMain.bUnConnectClick(Sender: TObject);
begin
  //отключаем таблицы
  fDM.CDSLichData.Close;
  fDM.CDSTelephones.Close;
  //отключаем DCOMConnection
  fDM.DCOMC.Close;
end;

Вначале мы закрываем наборы данных, после чего прерываем подключение к серверу. Теперь сохраните проект, скомпилируйте его и загрузите. Попробуйте отредактировать какую-нибудь запись. Затем нажмите "Обновить данные", либо последовательно, отключитесь от сервера и снова подключитесь к нему. Вы убедитесь, что сделанные изменения не сохранились. Это подтверждает, что при редактировании мы изменяем данные в кэше, а вовсе не на сервере. Если вы снова измените запись, затем нажмете "Сохранить изменения в базу", то изменения передадутся на сервер. Повторное обновление данных убедит вас в этом.

Сохранение данных в локальный файл, и чтение из файла

Теперь нам осталось написать обработчики для кнопок "Сохранить данные в файл" и "Загрузить данные из файла". Однако здесь следует сделать одно замечание. Как уже говорилось, метод SaveToFile () компонента ClientDataSet имеет два параметра: имя файла, куда нужно сохранять данные, и формат файла, который имеет тип TDataPacketFormat. Этот тип может содержать три значения (см. табл.29.2), описание этого типа находится в модуле DBClient. Этот модуль уже подключен к нашему модулю данных DM, однако он не подключен к главной форме. Следовательно, в главной форме мы не сможем реализовать сохранение данных в файл и чтение их из файла, если не подключим этот модуль в раздел uses. Чтобы дважды не подключать его к проекту, реализуем методы сохранения в файл и чтения из файла в модуле данных, а в главной форме просто вызовем их.

Перейдите к модулю данных. В разделе Public опишите две процедуры:

public
    { Public declarations }
    procedure SaveDataToFiles;
    procedure LoadDataFromFiles;

Установите курсор на одну из них и нажмите <Ctrl+Shift+C>, чтобы сгенерировать обе эти процедуры. Эти процедуры будут такими:

{Читаем данные из файла}
procedure TfDM.LoadDataFromFiles;
begin
  if OD1.Execute then begin
    CDSLichData.LoadFromFile(OD1.FileName);
    ShowMessage('Процедура чтения данных выполнена!');
  end; //if
end;
//**************************************
{Сохраняем данные в файл}
procedure TfDM.SaveDataToFiles;
begin
  if SD1.Execute then begin
    CDSLichData.SaveToFile(SD1.FileName, dfBinary);
    ShowMessage('Процедура сохранения данных выполнена!');
  end; //if
end;

Комментарии достаточно подробны и не нуждаются в дополнительных пояснениях. Код кнопки "Сохранить данные в файл" на главной форме просто вызывает нужную процедуру:

procedure TfMain.bSaveToFileClick(Sender: TObject);
begin
  fDM.SaveDataToFiles;
end;

А код кнопки "Загрузить данные из файла" будет следующим:

procedure TfMain.bLoadFromFileClick(Sender: TObject);
begin
  fDM.LoadDataFromFiles;
end;

Сохраните проект, скомпилируйте и загрузите его. Соединитесь с базой данных, чтобы считать данные. Сохраните их в файл и отключитесь от базы данных. Считайте данные из локального файла, не подключаясь к БД. Вы видите, что сохранились данные не только главной, но и подчиненной таблицы. Фактически, через поле TTelephones, каждая запись главной таблицы содержит также связанные данные из подчиненной таблицы, поэтому нам пришлось сохранять лишь один файл. Вы реализовали "метод Портфеля".

На этом наш курс "Программирование баз данных в Delphi " окончен. Желаю всевозможных успехов в программировании!

< Лекция 28 || Лекция 29: 12
Евгений Медведев
Евгений Медведев

В лекции №2 вставляю модуль данных. При попытке заменить name на  fDM выдает ошибку: "The project already contains a form or module named fDM!". Что делать? 

Анна Зеленина
Анна Зеленина

При вводе типов успешно сохраняется только 1я строчка. При попытке ввести второй тип вылезает сообщение об ошибке "project mymenu.exe raised exception class EOleException with message 'Microsoft Драйвер ODBC Paradox В операции должен использоваться обновляемый запрос'. 

Назерке Сейтсаданова
Назерке Сейтсаданова
Казахстан, Усть-Каменагорск, ВКГУ им. С.Аманжолова
Наталья Статченко
Наталья Статченко
Россия, Благовещенск