скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

Использование баз данных Paradox в локальной сети



Автор: Егоров Алексей

Хотелось бы поделиться опытом использования баз данных Paradox в локальной сети. По большому счету, принципы распределенного и локального доступа к таблицам Paradox ничем не отличаются. Исключения составляют некоторые правила и рекомендации, которые изложены ниже...

  1. В последнее время в сторону BDE раздается много замечаний и нареканий по поводу ограниченности и ненадежности при организации сетевого доступа к файлам БД.
    Существует множество решений и хитростей, призваных облегчить жизнь разработчику, работающему с BDE. Как показывает опыт, такие эксперименты чаще всего приводят к частичной и полной потере данных в файловых таблицах... Отсюда правило первое : если вы хотите использовать Paradox в сети, установите BDE в полном объеме на каждой клиентской машине, включая ту, где находятся файлы базы данных. Обязательны следующие установки в конфигурации на каждой машине :
    Это предотвратит Sharing Violation :
    BDE->Configuration->System->Init : LOCAL SHARE = TRUE 
    BDE->{Самое важное} 
    Это предотвратит неразбериху в местоположении данных.

    BDE не работает с базами данных напрямую, как например \\head\C\Database.
    Диск с данными необходимо подключить как сетевой. Еще лучше, если буквы сетевого диска будут одинаковыми на всех машинах...
    BDE->Configuration->Drivers->Native->Paradox : NETDIR = F:\Databse, 
    BDE->где F = \\head\c\	
    Возникает резонный вопрос : а что если баз данных несколько?
    Тогда в качестве NETDIR необходимо указать корневой каталог, в нашем случае F.
    Если вы работаете с файловыми базами данных, то вам необходимо воспринимать BDE как некоторый сервис, как скажем, interbase. Сравнение конечно не из лучших, однако нормально работающий BDE - залог сохранности и надежности ваших данных.

  2. К вопросу о Refresh.
    Если вы работаете локально, то никаких проблем с обновлением набора данных у вас не возникает. При изменении данных, вы сразу видите результат на экране. В сети ситуация несколько иная : пользователи на других компьютерах не видят тех изменений, которые происходят без их участия. Конечно, ничто не мешает нам через определенные интервалы времени обновлять элементы управления с помощью Refresh, однако не очень приятно наблюдать за тем, как ваша программа превращается в новогоднюю елку, постоянно мерцая элементами управления. Проблема решается с помощью BDE Callback Functions :
    // Объявления...
    TForm1 = class(TForm)
      // ...
    public
      function TableChangeCallBack(CBInfo: Pointer): CBRType;
      procedure UpdateTableData(var Msg: TMessage); message WM_UPDATETABLE;
      procedure TablesAfterOpen(DataSet: TDataSet);
    end;
    
    // Реализация...
    
    function TForm1.TableChangeCallBack(CBInfo: Pointer): CBRType;
    begin
      PostMessage(Handle, WM_UPDATETABLE, 0, 0);
    end;
    
    procedure TForm1.UpdateTableData(var Msg: TMessage);
    var
      Index: Integer;
    begin
      for Index := 0 to DBDataModule.ComponentCount - 1 do
        // DBDataModule - это Data module, в котором находятся все
        // TTable нашего   проекта...
        if DBDataModule.Components[Index] is TTable then
          if TTable(DBDataModule.Components[Index]).State = dsBrowse then
            TTable(DBDataModule.Components[Index]).Refresh;
    end;
    
    procedure TForm1.TablesAfterOpen(DataSet: TDataSet);
    begin
      TBDECallback.Create(Self, TTable(DataSet).Handle, cbTableChanged, nil, 0,
        TableChangeCallBack, True);
    end;
    // На событие OnCreate в Data Module подключаем наши функции к TTable...
    
    procedure TDBDataModule.DBDataModuleCreate(Sender: TObject);
    var
      Index: Integer;
    begin
      for Index := 0 to ComponentCount - 1 do
        if Components[Index] is TTable then
          TTable(Components[Index]).AfterOpen := Form1.TablesAfterOpen;
    end;
    Но и это еще не все...Теперь нам нужен TTimer..
    
    .procedure TForm1.FrashmanTimer(Sender: TObject);
    var
      Index: Integer;
    begin
      try
        for Index := 0 to DBDataModule.ComponentCount - 1 do
          if DBDataModule.Components[Index] is TTable then
            if (TTable(DBDataModule.Components[Index]).Active)
              and
              (TTable(DBDataModule.Components[Index]).State = dsBrowse) then
              DBIForceReRead(TTable(DBDataModule.Components[Index]).Handle);
      except
      end;
    end;

  3. Сохранность данных в сети.
    Для того чтобы быть уверенным в сохранности данных, необходимо на событие AfterPost Компонета TTable назначить следующее :
    
    dbiSaveChanges(TTable(DataSet).Handle);
    
    {Блокировки.Существует два подхода к разделенному
    изменению данных: оптимистический и писсимистический.В первом случае речь
    идет о клиент - сервере и транзакциях.Иначе говоря, сколько угодно
    пользователей могут одновременно изменять одни и те же данные.В нашем
    случае такой возможности просто не существует.Необходимо самим предусмотреть
    ситуацию, когда пользователи, например, попытаются одновременно редактировать
    одну и ту же запись.Ничего страшного, кроме сообщения "record locked by
    another user", не произойдет.Однако желательно самим обработать данную
    ситуацию.Вот пример функции, которая проверяет, заблокирована запись или нет:}
    
    function IsRecordLocked(Table: TTable): Boolean;
    var
      Locked: BOOL;
      hCur: hDBICur;
      rslt: DBIResult;
    begin
      Table.UpdateCursorPos;
      Check(DbiIsRecordLocked(Table.Handle, Locked));
      Result := Locked;
      if (Result = False) then
      begin
        Check(DbiCloneCursor(Table.Handle, False, False, hCur));
        try
          rslt := DbiGetRecord(hCur, dbiWRITELOCK, nil, nil);
          if rslt <> DBIERR_NONE then
          begin
            if HiByte(rslt) = ERRCAT_LOCKCONFLICT then
              Result := True
            else
              Check(rslt);
          end
          else
            Check(DbiRelRecordLock(hCur, False));
        finally
          Check(DbiCloseCursor(hCur));
        end;
      end;
    end;





Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook