Использование DB Controls без базы данных
От: Максим Гумеров Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 27.09.03 08:59
Оценка: 365 (10)
Статья:
Использование DB Controls без базы данных
Автор(ы): Максим Гумеров
Дата: 18.09.2003
Как обеспечить единообразную работу с данными, хранящимися в БД (DBase, Interbase, MS SQL Server и т.п.), и с другими, в частности, с локальными данными программы? В статье рассматривается создание потомка TDataSet для организации "виртуальной" БД на базе произвольного источника данных.


Авторы:
Максим Гумеров

Аннотация:
Как обеспечить единообразную работу с данными, хранящимися в БД (DBase, Interbase, MS SQL Server и т.п.), и с другими, в частности, с локальными данными программы? В статье рассматривается создание потомка TDataSet для организации "виртуальной" БД на базе произвольного источника данных.
Специалист — это варвар, невежество которого не всесторонне :)
Re: Использование DB Controls без базы данных
От: Аноним  
Дата: 10.10.03 05:05
Оценка:
Здравствуйте, Максим Гумеров.

Заинтересовался вашей статьей и решил попробовать Вашего наследника TDataSet.
При этом столкнулся с ошибкой при отображении даты.
Как правильно поместить значение даты в Data?
У меня такой код


TDateTime(Data) := TMyRecord(List[Index]).Date;


вызывает ошибку

---------------------------
Debugger Exception Notification
---------------------------
Project Project1.exe raised exception class EConvertError with message ''0.37904' is not a valid timestamp'. Process stopped. Use Step or Run to continue.
---------------------------
OK Help
---------------------------

Нашел, что ошибка возникает здесь:


  function NativeToDateTime(DataType: TFieldType; Data: TDateTimeRec): TDateTime;
  var
    TimeStamp: TTimeStamp;
  begin
    case DataType of
      ftDate:
        begin
          TimeStamp.Time := 0;
          TimeStamp.Date := Data.Date;
        end;
      ftTime:
        begin
          TimeStamp.Time := Data.Time;
          TimeStamp.Date := DateDelta;
        end;
    else
      try
        TimeStamp := MSecsToTimeStamp(Data.DateTime); // Попадает сюда
      except
        TimeStamp.Time := 0;
        TimeStamp.Date := 0;
      end;
    end;
    Result := TimeStampToDateTime(TimeStamp); // и здесь возникает ошибка 
  end;



function TimeStampToDateTime(const TimeStamp: TTimeStamp): TDateTime;
asm
        PUSH    EBX
{$IFDEF PIC}
        PUSH    EAX
        CALL    GetGOT
        MOV     EBX,EAX
        POP     EAX
{$ELSE}
        XOR     EBX,EBX
{$ENDIF}
        PUSH    EAX
        CALL    ValidateTimeStamp // Вот тут вот
        POP     EAX
        MOV     ECX,[EAX].TTimeStamp.Time
        MOV     EAX,[EAX].TTimeStamp.Date
        SUB     EAX,DateDelta
        IMUL    [EBX].IMSecsPerDay
        OR      EDX,EDX
        JNS     @@1
        SUB     EAX,ECX
        SBB     EDX,0
        JMP     @@2
@@1:    ADD     EAX,ECX
        ADC     EDX,0
@@2:    PUSH    EDX
        PUSH    EAX
        FILD    QWORD PTR [ESP]
        FDIV    [EBX].FMSecsPerDay
        ADD     ESP,8
        POP     EBX
end;



В чем причина, никак не могу понять.

С уважением, Артем.
Re[2]: Использование DB Controls без базы данных
От: Slicer [Wirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 10.10.03 07:55
Оценка:
Hello Artem.

А>TDateTime(Data) := TMyRecord(List[Index]).Date;


Voobshe-to, ya ne uveren, chto eto dolznho rabotat' kak zadumano. K sozhaleniyu, Delphi na etoy moey mashine ne stoit — burzhui poka ofis ne predostavili. Pridetsya gadat'.

Chto takoe "Data", kakoy u nego tip, gde ono nahoditsya? Ne sozdaetsya li pri TDateTime(Data) novaya lokalnaya peremennaya (to est' tuda li proishodit prisvaivanie, kuda nado, to est v Data). I gde proishodit prisvaivanie, v kakoy funczii?

Nu i potom — sama data-to pravilnaya? Naprimer, kakaya vyzyvaet oshibku?

Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Re[3]: Использование DB Controls без базы данных
От: Аноним  
Дата: 10.10.03 08:44
Оценка:
Здравствуйте, Slicer [Wirkwood]

Сама дата правильная. Создается как Now.
Но не работает с любой, какую я только не пробовал.

Параметр Data — нетипизированный. вот функция.
Если прочитаете статью, про которую идет речь, то в исходниках найдете метод GetFieldData.

Вот как он выглядит у меня:


procedure TMyData.GetFieldData(Index: integer; Field: TField; out Data);
begin
  if @Data = nil then Exit;
  case Field.FieldNo of
    1:strpcopy(@Data,TMyRecord(List[Index]).Name);
    2:strpcopy(@Data,TMyRecord(List[Index]).EMail);
    3: Integer(Data) := TMyRecord(List[Index]).Num;
//    4: System.Move(TMyRecord(List[Index]).Date, Data, sizeof(TDateTime));
//    4: Comp(Data) := TimeStampToMSecs(DateTimeToTimeStamp(TMyRecord(List[Index]).Date));
    4: TDateTime(Data) := TMyRecord(List[Index]).Date;
  end;
end;



С Integer все, как ни странно, работает. Чем TDateTime отличается? Ведь это же простой Double.

А Data — это просто буфер , куда TDataSet данные закачивает, как я понимаю, для отображения.
Хотя, возможно, я не прав.

Артем.
Re[4]: Использование DB Controls без базы данных
От: Slicer [Wirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 10.10.03 10:27
Оценка:
Nado podumat'. No bez Delphi pod rukoy trudno. Mozhet, poka kto drugoy otvetit?
A vy proverte, kstati, sovpadaet li data iz TMyRecord(...) s toy, kotoraya prihodit v "TimeStamp := MSecsToTimeStamp(Data.DateTime);" v pole Data.DateTime.

Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Re[5]: Использование DB Controls без базы данных
От: Аноним  
Дата: 10.10.03 10:49
Оценка:
Проверил. Да и раньше проверял.

Сюда передается дата в виде 37904.607452, что соответствует DateTimeToStr(Date.DateTime) = 10.10.2003 14:34:16

после этого метода


TimeStamp := MSecsToTimeStamp(Data.DateTime)


в TimeStamp содержится TimeStamp(Time: 37905; Date:0), что уже странно.
Сама функция на asm, поэтому я в ней ничего не понимаю.

И потом при вызове
Result := TimeStampToDateTime(TimeStamp)

генерится сообщение

---------------------------
Debugger Exception Notification
---------------------------
Project Project1.exe raised exception class EConvertError with message ''0.37905' is not a valid timestamp'. Process stopped. Use Step or Run to continue.
---------------------------
OK Help
---------------------------

что, видимо правильно, т.к. у меня значение даты округлилось до целых и записалось в поле TTimeStamp.Time почему-то.

Хотя авто в статье явно пишет, что для полей с типом ftDateTime используется тип TDateTime.

В общем, так я ничего и не понял, в чем же дело. Кстати, если кто будет пробоват это пример с TDBGridEh,
то имейте в виду, при вызове метода TField.IsNull валится AV, поэтому в методе GetFieldData надо вставит строку
if @Data = nil then Exit, Это связано с тем, что в качестве параметра Data передается nil.
А сам Exception возникает в другом месте — в функции strlcopy, поэтому сначала непонятно, что случилось.
Кстати, это замечание и автору. Надо-бы подправить примерчик.
Re[6]: Использование DB Controls без базы данных
От: Аноним  
Дата: 10.10.03 11:17
Оценка:
Хм, Максим, извините.
Никак не возьму в привычку смотреть профиль участника.
По нику вас не опознал, т.к. это мои первые посты здесь.

Еще раз сорри.

Артем.
Re[7]: Использование DB Controls без базы данных
От: Slicer [Wirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 10.10.03 12:58
Оценка:
Nichego strashnogo
V obshem, primer peresmotryu, kak doberus' do Delphi. A vot naschet TDateTime eto ya otkuda-to vzyal, vidimo, iz ishodnikov VCL. Tak chto ya ne vinovat Da i po kodu pervoy privedennoy vami funczii ponyatno, chto v 3-em variante on hochet poyuzat' i date, i time, a predydushie 2 varianta sootvetstvovali ftDate i ftTime. V chem oshibka, ne znayu, no ved' data-to prihodit pravilnaya, a exception vyletaet uzhe ne v moem kode...

Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Re[7]: Использование DB Controls без базы данных
От: Slicer [Mirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 20.10.03 12:14
Оценка:
Артем, киньте, пожалуйста, ваш исходник сюда: gumerov@mail15.com.
Самому писать времени нет

Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Re[6]: Использование DB Controls без базы данных
От: kavlad Россия http://www.wavesoft.ru
Дата: 22.10.03 05:42
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Проверил. Да и раньше проверял.


А>Сюда передается дата в виде 37904.607452, что соответствует DateTimeToStr(Date.DateTime) = 10.10.2003 14:34:16


А>после этого метода



А>
А>TimeStamp := MSecsToTimeStamp(Data.DateTime)
А>


А>в TimeStamp содержится TimeStamp(Time: 37905; Date:0), что уже странно.

А>Сама функция на asm, поэтому я в ней ничего не понимаю.

Вообще-то надо исполльзовать DateTimeToTimeStamp.
Калабухов А.В.
Re[7]: Использование DB Controls без базы данных
От: Аноним  
Дата: 23.10.03 04:58
Оценка:
Здравствуйте, kavlad.

Я тоже так думаю, но ребята из Borland думают иначе .
Re: Использование DB Controls без базы данных
От: Slicer [Mirkwood] Россия https://ru.linkedin.com/in/maksim-gumerov-039a701b
Дата: 25.03.04 10:51
Оценка:
В статью внесены два исправления, см. код методов DataSet3.TMyDataSet.GetFieldData (спасибо kavlad) и DataSet3.TMyDataSet.InternalDelete.

Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Re: Использование DB Controls без базы данных
От: kavlad Россия http://www.wavesoft.ru
Дата: 24.09.04 08:04
Оценка: +1
Здравствуйте, Максим Гумеров, Вы писали:

Максим, я не разобрался, как в GetFieldData выводить Null значения в предложенной схеме.
Помогла простая переделка:

  FPerformGetFieldData:procedure (Index:integer; Field: TField; out Data) of object;


заменил на

  FPerformGetFieldData:function (Index:integer; Field: TField; out Data): Boolean of object;


Если значение поля NULL, то результат будет False.
... По ушам лупит "начальство" ...
Re: Использование DB Controls без базы данных
От: kavlad Россия http://www.wavesoft.ru
Дата: 05.03.05 11:48
Оценка: 8 (1)
Здравствуйте, Максим Гумеров, Вы писали:

Я обнаружил ошибку в Вашем коде. Она проявляется таким
образом — если сразу после запуска тестового проекта
вставить новую запись, то происходит AV.
Если же сначала отредактировать запись или додавить Append'ом, то последующие
Inset'ы проходят нормально.
Это связано с ошибкой при создания нового буфера.
Буфер для новой записи не инициализируется и его поля RecordIndex и BookmarkFlag указывают в никуда. Поэтому при обновлении данных на экране (для грида) и происходи AV.

Лечится простой доработкой:

function TMyDataSet.AllocRecordBuffer: PChar;
begin
  GetMem(result,sizeof(TRecordBuffer));
  PRecordBuffer(Result)^.RecordIndex := 0;
  PRecordBuffer(Result)^.BookmarkFlag := bfCurrent;
end;
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.