Средства для работы с таблицами (курсорами)
Представляю вниманию посетителей клуба обновленный вариант библиотеки классов для работы с постоянными и времеными таблицами (курсорами) и наборами таблиц.
По сравнению с первым вариантом здесь добавлен доступ к свойствам и значения полей курсора (Класс Field). Для класса DataCursor добавлены свойства Fields, nFieldsCount, cFieldClass и метод MakeFields, метод Go переименован в Navigate, добавлены методы Go, GoTop, GoBottom, Skip, Seek, Locate, Continue.
Изменения сделаны по результатам обсуждения с Дмитрием Орловым (Jimmy)
Теперь библиотека содержит три класса: DataCursor, Field и DataSet.
Класс DataCursor обеспечивает открытие и закрытие таблиц (курсоров), управление индексами, фильтрами, буферизацией, связями с другими таблицами, а также перемещение по таблице.
Класс Field обеспечивает доступ к свойствам и значениям полей курсора. Объекты класса Field создаются как объекты-члены в объекте DataCursor и заносятся в массив Fields, являющийся свойством объекта DataCursor.
Класс DataSet обеспечивает совместную работу с несколькими связанными по смыслу курсорами. Предполагается, что набор данных включает в себя несколько курсоров (класс DataCursor и его производные) и наборов данных (класс DataSet и его производные), работа с которыми должна выполняться согласовано.
Предлагаемая библиотека не является в полном смысле "Решением", так как ни в одной реальной
задаче не обкатывалась. Это скорее эскиз решения, идея, которую я хотел бы обсудить на
профессиональном уровне прежде, чем окончательно вставлять эти средства в свои проекты. Кроме
того, не обладая опытом работы с SQL-серверами и представлениями, я не включил в эти классы
возможности работы с ними, а хотелось бы.
Суть предложения заключается в том, чтобы организовать в клубе или по e-mail обсуждение и,
возможно, совместное доведение библиотеки до кондиции с тем, чтобы в последствии выложить
получившийся продукт в клубе для всеобщего использования.
Со своей стороны, я готов взять на себя всю организационную сторону дела - учет предложений,
адаптацию их в исходный текст и обновление выложенной в клубе библиотеки.
Более подробная информация о мотивах, классах и содержании пакета - в файле readme.txt
Заинтересовавшиеся моим предложением посетители клуба могут высказать свое мнение здесь или по
e-mail olegvbru@yahoo.com
С уважением, Олег Бляхеров.
Файл cursors.prg в примере никак не используется. Он остался в проекте по недосмотру и Вы его
можете спокойно удалить. Сорри!
Дмитрий Орлов
14.02.02 12:14:46
Олег, посмотрел я твои классы. Идею поддерживаю полностью. Но все же имею несколько
субъективных замечаний.
Поэтому, изложу их в ключе "Если бы это делал я". Итак:
1. Обязательно добавил бы в DataCursor коллекцию Fields[], содержащую значения полей текущей
записи.
2. Вкупе с Fields[] необходимо свойство nFieldsCount - количество полей в таблице
3. Раз уж есть количество полей, то неплохо и количество записей узнать - nRecCount
4. Изменил бы базовый класс на Container, что позволило бы класть его на форму и добавлять туда
контроля для работы с данными.
Тогда можно было бы пользоваться такими приемами:
Для MyControl:
PROCEDURE Refresh
THIS.Value = THIS.Parent.Fields[1]
ENDPROC
и одновременно рефрешить все значения. Или
PROCEDURE Valid
THIS.Parent.Fields[1] = THIS.Parent
ENDPROC
Т.е. упростить разработку форм редактирования данных. Тут возможно много других вариаций,
вплоть до создания специальных методов для формирования списков.
5. И все же разгрузил бы метод Go() таким образом:
- создал бы protected метод Navigate() - полный аналог твоего нынешнего Go()
- ну и привычные всем нам Find(), Go(), Locate(), GoTop(), GoBottom() и т.п., в теле которых
вызывал бы метод Navigate() с нужными параметрами. Таким образом, упростилась бы навигация
и сохранилсь бы централизованная обработка перемещений указателя.
ИМХО если уж делать класс-оболочку, то нужно сконцентрировать в нем всю функциональность.
Такой мой принцип. Пусть не все будет использовано, но будет выбор - традиционно или классово :0)
Олег Бляхеров
14.02.02 18:57:10
Насчет Fields, FieldsCount, Go, RecCount - классная мысль, жаль, что самому в голову не пришло.
Для Fields возможны два варианта: массив объектов класса Field с свойствами Name, Type, Size,
Frac, Value и т.п. или массив значений полей. Первый вариант кажется более "крутым". Придумать бы
еще как настоящую коллекцию на VFP сделать! Чтобы можно было написать Fields(1).Name или
Fields("F1").Value и Fields.Count.
Насчет замены базового класса на Container не совсем понятно: во-первых, у контейнера нет Caption
и на форме не будет видно, о каком курсоре идет речь, во-вторых, на форме могут быть рядом поля из
разных курсоров - контейнеры будут перекрывать друг друга. Или я чего-то не понял?
Спасибо за комментарии!
Дмитрий Орлов
15.02.02 12:27:55
Вторая серия.
-------------
Насчет Fields[]:
Коллекцию объектов как раз и можно внедрить в Contaner!
Нужно добавить в библиотеку класс Field, а в DataCursor добавлять его экземпляры и
инициализировать их.
Попутно присваивать ссылки на них свойствам Fields[ x ]. Тогда и будет полноценная коллекция.
Насчет Caption:
Как я понял, Label удобен тебе тем, что на форме видно, какие курсоры там "лежат".
Так вот: а что мешает внедрить Label в DataCursor (Container!!!), добавить в DataCursor
свойство Caption и с помощью метода Caption_assign() присваивать его значение Caption'у Label'а?
ЗЫ Если же на форме лежат контролы для нескольких курсоров, то можно и НЕ ИСПОЛЬЗОВАТЬ трюк с
внедрениеем :0))
Дмитрий Орлов
15.02.02 13:25:30
Уточнение.
----------
Чтобы избежать двоякости, следует читать так:
"ЗЫ Если же на форме лежат контролы для нескольких
курсоровров, то можно и НЕ ИСПОЛЬЗОВАТЬ трюк с
внедрением (контролов в контейнер DataCursor)"
Олег Бляхеров
15.02.02 15:03:15
Принято! Через пару дней выложу новый вариант.
Дмитрий Орлов
01.03.02 13:26:05
Олег, сообщи пожалуйста, когда будет готово.
Олег Бляхеров
06.03.02 06:54:30
Привет, Jimmy!
Сегодня новый вариант загрузил.
Получилось красиво (как мне каэться), но не очень понятно зачем и куда дальше?
Я что имею в виду - классы DataCursor и DataSet появились не от хорошей жизни, а чтобы закрыть
имеющуюся дыру в инструменте и предоставить новые функциональные возможности. Класс Field в этом
смысле вещь необязательная. Т.е. выбор между FCOUNT(oCursor.cAlias) и oCursor.nFieldsCount или
между FSIZE("Fld", oCursor.cAlias) и oCursor.Fields("Fld").FldWidth (oCursor._Fld.FldWidth)
определяется исключительно вкусом и личными пристрастиями. По-крайней мере, до меня абсолютная
необходимость в новом классе пока не дошла.
Если двигаться дальше в этом направлении, то нужно добавлять возможность модификации структуры
таблиц путем изменения свойств объекта Field (скажем при oCursors.Fields("Fld").FldWidth = 100 ),
вводить объекты TAG со всеми свойствами, менять Eof(oCursor.cAlias) на oCursor.Eof и т.д, и т.п.
Т.е. менять практически всю языковыую среду в части доступа к данным. Я пока к новациям такого
масштаба морально не готов. И не понимаю, что я от этого получу, кроме красоты текста (хотя она и
люба моему сердцу).
Один резон, конечно, есть. Языковые возможности Fox'а настолько страдают чрезмерным разнообразием
и эклектичностью (дань тяжелому наследию dBase), что часто ставят в тупик даже опытных
программистов. Тут тебе и AFIELDS с последующим ковырянием в массиве, и CURSORGETPROP, и
многословный ALTER TABLE, и, ни в какие ворота не укладывающиеся, многочисленные SYS. Недавно,
например, я несколько дней (неподряд, конечно) искал функцию GETCLASS (по аналогии с GETFILE и
т.п.), даже на форуме спрашивал. Через месяц случайно наткнулся на нее в help - оказывается она
называется AGETCLASS, т.к. она, видите-ли массив из двух элементов возвращает!
В этом смысле, перефразирование всего этого в стиле ООП возможно даст свой эффект. Но на это нужна
масса времени для проработки вариантов и экспериментов, а его, как известно, нет...
Мне же хочется двигаться в направлении SQL-серверных решений и здесь твоя библиотека оказывается
весьма кстати. У меня сразу возникло желание их как-нибудь подружить. Правда, из-за отсутствия
практики не очень понятно как?
В частности, не понятно зачем в одном объекте аккумулировать все виды возможных соединений, а
потом выделять из них активное. Разве наличие у приложения двух и более разнородных соединений
столь часто встречается? И, наверное, дилетантский вопрос: при работе с SQL-сервером достаточно
одного соединения для всего приложения или их требуется несколько?
Т.е. если бы это делал я, то сделал бы класс CONNECTION, обеспечивающий _одно_ соединение с
указанным источником, а при необходимости иметь несколько соединений - создавал бы нужное
количество объектов и, при необходимости, устроил бы из них коллекцию в менеджере приложения.
Ссылку на такой объект можно было бы внедрять в класс DataCursor, точнее в его наследник
SQLDataCursor, который бы с помощью средств класса cstSQLPassTrough обеспечивал бы работу с
таблицей на сервере.
Дмитрий Орлов
06.03.02 06:55:44
Привет Олег.
1.Насчет необходимости класса Fields и пр. - вопрос конечно спорный,
но ИМХО это лучше укладывается в концепцию ООП, чем оперирование
функциями.
2. Насчет коллекции DSN в моей библиотеке: основная цель ее создания -
именно возможность построения их списка. Например, я делал CS
приложение, где
при первом запуске необходимо указать имя DSN.
Так вот: там жедательно предлагать их список, а не надеяться, что юзер
помнит,
как он называется, этот DSN.
Ну и, плюс к этому, возможны варианты, когда приложение работает с
несколькими
разными серверами(например для импорта/экспорта данных, да еще для
разных СУБД),
тогда и будет тот случай, когда используется несколько подключений.
3. Насчет "скрещивания" наших классов я тоже думал. Как
определюсь и сформулирую, так и кину.
4. Кстати, предлагаю это обсуждение перенести на сайт, чтобы другие
посмотрели-подумали.