Админ

четверг, 1 сентября 2016 г.

Библиотека EhLib (Delphi): ошибка при нумерации записей в TMemTableEh

|
Библиотека EhLib — это один из тех наборов компонентов Delphi, которые позволяют упростить создание удобных приложений для работы с базами данных. В ней предусмотрено довольно многое из того, чему было бы самое место в стандартном наборе VCL: разноцветные таблицы с многоуровневыми заголовками и динамически меняющейся высотой строк, компонент-календарик для ввода дат, ComboBox, в котором можно растягивать выпадающий список мышкой, и многое другое.


Особняком в этом наборе стоят компоненты для работы с наборами данных в памяти: TMemTableEh и различные вариации T…ConnectionProvider / T…DataDriver к нему. Эти компоненты удобны тем, что позволяют кэшировать на локальной машине наборы данных с сервера и работать с ними, используя как стандартные методы TDataSet, так и собственные поля и методы, обеспечивающие мгновенный доступ к данным в памяти. Вот в последних и кроется подвох.

Дело в том, что в компоненте TDataSetTMemTableEh как его потомке) нумерация записей в наборе данных идёт с единицы (свойство 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]) позиционируют курсор на разных записях. Если первый метод работает именно так, как ожидалось, то второй переходит не на ту запись, которая нам нужна, а на предыдущую (да, и если мы попробуем таким способом получить месячную зарплату директора в долларах, то его ждёт неприятный сюрприз):


Комментариев нет:

Отправить комментарий

Пожалуйста, не используйте в сообщениях ненормативную лексику и нарушающие закон темы

К началу