Библиотека EhLib — это один из тех наборов компонентов Delphi, которые позволяют упростить создание удобных приложений для работы с базами данных. В ней предусмотрено довольно многое из того, чему было бы самое место в стандартном наборе VCL: разноцветные таблицы с многоуровневыми заголовками и динамически меняющейся высотой строк, компонент-календарик для ввода дат, ComboBox, в котором можно растягивать выпадающий список мышкой, и многое другое.
Особняком в этом наборе стоят компоненты для работы с наборами данных в памяти:
TMemTableEh
и различные вариации T…ConnectionProvider / T…DataDriver
к нему. Эти компоненты удобны тем, что позволяют кэшировать на локальной машине наборы данных с сервера и работать с ними, используя как стандартные методы TDataSet
, так и собственные поля и методы, обеспечивающие мгновенный доступ к данным в памяти. Вот в последних и кроется подвох.
Дело в том, что в компоненте
TDataSet
(и TMemTableEh
как его потомке) нумерация записей в наборе данных идёт с единицы (свойство RecNo
возвращает число в промежутке между 1 и RecordCount
). А собственные методы TMemTableEh
(FindRec
, InstantReadEnter
) используют нумерацию записи с нуля, то есть номер будет от 0 до RecordCount
− 1. В документации EhLib (я смотрел документацию от версии 7) это никак не отражено.
Попытка одновременно использовать унаследованное свойство
RecNo
и новые свойства и методы компонента TMemTableEh
без учёта этой особенности гарантированно ведёт к ошибкам, которые будет очень трудно диагностировать и исправить, если не знать о ней (собственно, и написать эту статью меня заставила многочасовая отладка одного приложения, в котором эти свойства и методы использовалось вперемешку).
Проверить это легко. Рассмотрим простое тестовое приложение (можно скачать с исходниками тут). Здесь используются и унаследованные от
TDataSet
поле и метод (RecNo
и Locate
), и специфические для TMemTable
(InstantReadEnter
и FindRec
). Видно, что попытка соединить их между собой обречена на ошибку. Например, Locate('NAME', 'Goldman', [loPartialKey, loCaseInsensitive])
и RecNo := FindRec('NAME', 'Goldman', [loPartialKey, loCaseInsensitive])
позиционируют курсор на разных записях. Если первый метод работает именно так, как ожидалось, то второй переходит не на ту запись, которая нам нужна, а на предыдущую (да, и если мы попробуем таким способом получить месячную зарплату директора в долларах, то его ждёт неприятный сюрприз):
Комментариев нет:
Отправить комментарий
Пожалуйста, не используйте в сообщениях ненормативную лексику и нарушающие закон темы