Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Использование баз данных Paradox в локальной сети

Оформил: DeeCo

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

Хотелось бы поделиться опытом использования баз данных 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;
Для полного счастья неплохая ссылка на ресурс с функциями BDE плюс примеры их использования
http://www.priv.bus.at/graf/quell/delphiarchives/bdehlp32/contentpage.htm
Проект Delphi World © Выпуск 2002 - 2004
Автор проекта: ___Nikolay