Web Services и MS Visual FoxPro

Файлы

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

1) Идентифицировать проблему (или постановить задачи и найти ответы на вопросы: “ Что мы хотим сделать и что должно в итоге получиться?”, “Какие ограничения или другие условия влияют на проект?”)

2) Сделать основной дизайн (решить какие функции и возможности мы должны будем разработать в системе, чтобы решить стоящую перед нами задачу). Понять в общем как система будет работать, не вдаваясь в излишние детали.

3) Создать наше рабочее приложение (написать код и выполнить другие виды работ, которые позволят сделать наш проект рабочим).

Кроме того, в основе нашего приложения будет лежать идея многослойности (n-tier): слой базы данных, своеобразный слой бизнес – логики (эту роль будет играть Service) и презентационный слой (собственно клиентская часть – то что увидят наши клиенты). Идея, лежащая в основе многослойных приложений, проста — каждый слой можно разрабатывать и модернизировать в будущем по отдельности. Из набивших уже «оскомину» на ум сразу приходит пример — заменить базу данных FoxPro на базу данных MS SQL Server… Это, наверное, самый популярный пример, так как зарплата программистов со знанием этих SQL серверов почему-то намного выше нашей, хотя работать с последним намного легче, чем с родными базами FoxPro. Стрнно все это, куда катится наш мир?

К недостаткам данного подхода необходимо отнести его затратность: чтобы внести изменения в программу, надо их внести в несколько мест. Так, например, для сайта http://www.sergey.co.uk/ (да, этот сайт разработан как обычная программа ASP.NET и подхода n-tire) если я захочу изменить, например, форум , я должен буду внести измения в базу данных, хранимые процедуры, слой работы с данными, бизнес-слой и , наконец, в презентационный слой. Что и говорить, кошмары современного метода разработки приложений, или добро пожаловать в мир, где все борются с безработицей подобным странным образом.

Проблема.

Главная и основная задача — создать приложение, которое бы послужило базой для дальнейшей самостоятельной работы программиста FoxPro. Отсюда вытекают остальные проблемы:
создать систему обмена сообщений внутри фирмы
возможность ведения частных и приватных диалогов
наличие специальных администраторов
совмещенная экранная форма в приложении – для администратора и клиента (у администратора добавляются по мере необходимости дополнительные объекты управления)
стандартный набор функций клиента:
регистрация (для простоты пароли будем хранить в открытом виде)
публиковать свои сообщения: всем (ALL) или определенному лицу (выбор из списка – в качестве параметра передовать на сервер начальные буквы User_nick)
отвечать на общие и приватные сообщения
вся необходимая информация о других клиентах будет предоставлена в Grid

стандартный набор функций администратора:
добавлять пользователей и изменять их профайлы
удалять и редактировать любые сообщения
блокировать пользователей
наличие на форме специального фильтра, ограничивающего период запрашиваемых с сервера сообщений

Дизайн. Создание базы данных.

В последнее время очень модно стало для разработки моделей баз данных использование всевозможных средств визуального моделирования даже на основе языка UML. Ваш покорный слуга даже написал на эту тему диссертацию, и чтоб не провалить ее окончательно присоединился к группе товарищей, которые предложили специальное расширение UML, чтобы с его помощью можно было создавать графические модели баз данных.

В этом направлении все сильно запущено, но все-таки воспользуемся привычным для западного человека графическим изображением модели базы данных, применив для этого ERM диаграмму и условно-бесплатный редактор QSEE Superlight. В общем-то ничего сложного для своего примера мы брать и не собирались:

Как мы видим из приведенной диаграммы, у нас будет три сущности (entity), связанные соотношениями (relationships) один ко многим. Имена атрибутов и их тип видны на рисунке. Далее, как любят показывать в рекламе, нажимается кнопка и генерируется код создания базы данных. Но не все так просто — этот код не всегда именно то, что нам надо, и приходится, как правило, довольно много потом исправлять руками. Да и наша мысль не стоит на месте. Изменения легче внести руками в готовую базу, чем рисовать это в ERM, а потом снова генерировать, исправлять… Короче, замкнутый круг на любителя. Для меня, как практика, эти диаграммы являются хорошим подспорьем для начальных бесед с клиентом. Обилие непонятных слов и картинки, как правило, производят на него неизгладимые впечатления, парализуют его мозг и позволяют легче выбивать из него деньги (может поэтому специалисты Oracle, DB2 «гребут деньги лопатой» за гораздо меньший труд, чем программисты FoxPro, что умеют «замутить» подобным образом мозги заказчику).
Для простоты повторения мы рекомендуем Вам создать аналогичную структуру директориев, как и в нашем проекте. Для серверной части, например: C:\WS_MESSAGE\SERVER\
По причинам указанным выше (то есть как это принято у «серьезных админов» баз данных) приведу создание таблиц нашей базы данных в коде, запустив который Вы создадите требуемую базу данных и заполните ее некоторыми начальными значениями. Код прозрачный, так что копируете и вставляете в окно редактора программ FoxPro: */—————————————————————————/*
*
* MS VFP version..: 9.0 (так как применим новые данные совместимые с SQL Server)
* Program-ID……: DB_CREATE.PRG
* Purpose………: Создание базы данных для проекта обмена сообщениями внутри
* компании. Данная база данных должна находится в дирректории
* где Ваш Web Service (это сделано только для простоты
* понимания данного проекта)
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 22/05/2005
* Last edited…..: 07/06/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
SET SAFETY OFF
CLOSE DATABASES ALL
PUBLIC m.pcpath
m.pcpath=’C:\WS_MESSAGE\SERVER\’
* Так-как у нас SET SAFETY OFF
* предыдущая база будет удалена без предупреждения
IF FILE(m.pcpath+’\DBWS.DBC’)
DELETE DATABASE (m.pcpath+’DBWS’) DELETETABLES
ENDIF
* собственно создаем базу данных
CREATE DATABASE (m.pcpath+’DBWS’)

CREATE TABLE m.pcpath+’USERS’ CODEPAGE=1251( ;
User_ID INTEGER NOT NULL AUTOINC NEXTVALUE 1 STEP 1 PRIMARY KEY, ;
User_nick CHARACTER(10) NOT NULL, ;
PASSWORD CHARACTER(10) NOT NULL, ;
email VARCHAR(40), ;
city VARCHAR(30), ;
admin NUMERIC(1,0), ;
isblocked NUMERIC(1,0), ;
register T NOT NULL, ;
lastvisit T ;
)

* В нижеследующей таблице устанавливаем постоянные соотношения
* один ко многим с таблицей User
* хотя, в принципе, они нам и не нужны
CREATE TABLE m.pcpath+’Mes_Header’ CODEPAGE=1251( ;
Mes_ID INTEGER NOT NULL AUTOINC NEXTVALUE 1 STEP 1 PRIMARY KEY , ;
User_from INTEGER NOT NULL REFERENCES USERS TAG User_ID, ;
User_to INTEGER NOT NULL REFERENCES USERS TAG User_ID, ;
WASUPDATED T NOT NULL, ;
TITLE VARCHAR(100), ;
published T NOT NULL ;
)
INDEX on TTOD(WASUPDATED) TAG WASUPDATED ADDITIVE

* здесь устанавливаем постоянное соотношение
* один ко многим с таблицей Mes_Header
* применение типа Blob для Message устраняет много проблем в будущем
* при передаче данных
CREATE TABLE m.pcpath+’Mes_body’ CODEPAGE=1251( ;
Reply_ID INTEGER NOT NULL AUTOINC NEXTVALUE 1 STEP 1 PRIMARY KEY, ;
Mes_ID INTEGER NOT NULL REFERENCES Mes_Header TAG Mes_ID, ;
User_from INTEGER NOT NULL REFERENCES USERS TAG User_ID, ;
MESSAGE W, ;
published T ;
)

* добавим сразу несколько необходимых записей
* Пользователь ALL (или вроде как код для широковещательного сообщения
* очень важно ввести его первой записью, чтобы код был равен 1)
* пароль выбираем случайным образом, чтобы никто не писал от его имени
INSERT INTO (m.pcpath+’USERS’) (User_nick, PASSWORD,admin ,register , lastvisit ) ;
VALUES;
(‘ALL’,SYS(2015),0,DATETIME(),DATETIME())
* администратора (для реальной системы рекомендуется сменить пароль)
INSERT INTO (m.pcpath+’USERS’) (User_nick, PASSWORD,admin ,register , lastvisit ) ;
VALUES;
(‘admin’,’admin’,1,DATETIME(),DATETIME())
* добавляем просто пользователя для тестирования
* здесь можете добавить себя
INSERT INTO (m.pcpath+’USERS’) (User_nick, PASSWORD,admin ,register , lastvisit ) ;
VALUES;
(‘sergey’,’sergey’,1,DATETIME(),DATETIME())
* теперь добавим тестовое сообщение
INSERT INTO (m.pcpath+’Mes_Header’) (User_from , User_to, WASUPDATED, TITLE, published) ;
VALUES ;
(2,1,DATETIME(),’Добро пожаловать в систему обмена сообщениями’, DATETIME())
INSERT INTO (m.pcpath+’Mes_body’) (Mes_ID,User_from ,MESSAGE,published ) ;
VALUES ;
(1,2,’Собственно поздравление.’+CHR(13)+’Надеемся, что все будет работать.’, DATETIME())

* покажем, что у нас получилось
*DISPLAY TABLES
*DISPLAY DATABASE
* чистим за собой
CLOSE DATABASES

DB_CREATE.PRG

Как мы видим, ничего сложного. Только пара нюансов — использование явного указания кодовой страницы и применение новго типа данных Blob. К сожалению, они доступны только начиная с версии VFP 9.0.

Далее создаем хранимые процедуры — «рабочие лошадки» нашего приложения. Существует мнение, что работать с базами данных следует только через хранимые процедуры. В принципе, хороший похдод, тем более, что все изменения в структуре Ваших данных и даже смену СУБД можно производить без смены клиента. Еще одно удобство — это ориентированность на наше WEB приложение (простота и прозрачность): клиент послал запрос в виде параметров хранимой процедуры, а в ответ получил результат. Все просто и надежно. Думаю, начав подобным образом разрабатывать приложения, Вы выбьете почву из под противников FoxPro, ибо они используют аналогичный подход и очень этим гордятся.

Все хранимые процедуры начинаются с SP (это перевод на английский ХП — Stored Procedures). Далее в названии используется английское наименование функции (для больших задач рекомендуется после SP использовать имя модуля, а затем название выполняемой функции). Теперь небольшое лирическое отступление об использовании английского языка. В принципе, можно использовать и русский, но столкнувшись с некоторыми проблемами совместимости, я пришел к выводу — использовать все-таки английский. Это хорошая практика, так как неизвестно, куда Вас закинет судьба работать завтра — может в ОАЭ, где никто не говорит по-русски, но смогут понять Ваш код на английском.

Приведу текст всех Хранимых Процедур: */—————————————————————————/*
* Текст всех ХП находится в sp_procedures.txt
* Для создания всех ХП из текстового файла запустите Create_SP.PRG
*/—————————————————————————/*

*/—————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: sp_chk_admin.PRG
* Purpose………: Проверка на наличие прав администратора по user_id
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 22/05/2005
* Last edited…..: 22/05/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PROCEDURE sp_chk_admin
PARAMETERS m.lcuser_id
SET DELETED ON
PRIVATE m.lnreturn_parameter
m.lnreturn_parameter=-1
IF m.lcuser_id>0
SELECT admin FROM USERS WHERE User_ID=m.lcuser_id INTO CURSOR user_exists
IF user_exists.admin >0
m.lnreturn_parameter=user_exists.admin
ENDIF
ENDIF
IF USED(‘USERS’)
USE IN USERS
ENDIF
IF USED(‘USER_EXISTS’)
USE IN user_exists
ENDIF

RETURN m.lnreturn_parameter

*/—————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: sp_message_read.PRG
* Purpose………: Чтение заголовков сообщений с сервера
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 22/05/2005
* Last edited…..: 22/05/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PROCEDURE sp_message_read
PARAMETERS m.lddate, m.lcUser_nick, m.lcpassword, m.lndays_show
SET DELETED ON
PRIVATE m.lnuser_id, m.lnadmin, lcXML
STORE 0 TO m.lnadmin, m.lnuser_id
m.lnuser_id=sp_user_login_id(m.lcUser_nick, m.lcpassword)
lcXML=-1 && в случае неуспеха ответ в числовом виде
IF m.lnuser_id>0
m.lnadmin=sp_chk_admin(m.lnuser_id)
IF m.lnadmin>0 && это администратор, значит есть доступ ко всем сообщениям
SELECT Mes_Header.*,User_nick, city FROM Mes_Header ;
LEFT OUTER JOIN USERS ON Mes_Header.User_from=USERS.User_ID ;
WHERE TTOD(Mes_Header.WASUPDATED) >= ;
DATE(YEAR(m.lddate),MONTH(m.lddate),DAY(m.lddate)) — m.lndays_show ;
INTO CURSOR CUR_TEMP &&
ELSE && обычный пользователь
SELECT Mes_Header.*,User_nick, city FROM Mes_Header ;
LEFT OUTER JOIN USERS ON Mes_Header.User_from=USERS.User_ID ;
WHERE (Mes_Header.User_to=1 OR Mes_Header.User_to=m.lnuser_id or ;
Mes_Header.User_from=m.lnuser_id) AND ;
TTOD(Mes_Header.WASUPDATED) >= ;
DATE(YEAR(m.lddate),MONTH(m.lddate),DAY(m.lddate)) — m.lndays_show ;
INTO CURSOR CUR_TEMP &&
ENDIF
SELECT CUR_TEMP
IF RECCOUNT()>0
CURSORTOXML(«CUR_TEMP»,»lcXML»,1,1,0,»1″)
ENDIF
ENDIF
CLOSE TABLES ALL
RETURN lcXML

*/—————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: sp_user_login_xml.PRG
* Purpose………: Проверка пароля и имени пользователя
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 22/05/2005
* Last edited…..: 06/05/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PROCEDURE sp_user_login_xml
PARAMETERS m.User_nick, m.PASSWORD
SET DELETED ON
PRIVATE m.lcXML
m.lcXML=-1 && в случае неуспеха ответ в числовом виде
IF LEN(m.User_nick)>=3 AND LEN(m.PASSWORD)>3
SELECT User_ID, User_nick, admin, ISBLOCKED FROM USERS WHERE ;
UPPER(USERS.User_nick)==UPPER(m.User_nick) ;
AND UPPER(USERS.PASSWORD)==UPPER(m.PASSWORD) INTO CURSOR user_exists
IF RECCOUNT(‘USER_EXISTS’)>0 AND EMPTY(user_exists.ISBLOCKED)
* у нас есть такой пользователь
CURSORTOXML(‘user_exists’,»lcXML»,1,1,0,»1″)
ENDIF
ENDIF
IF USED(‘USER_EXISTS’)
USE IN user_exists
ENDIF
IF USED(‘USERS’)
USE IN USERS
ENDIF
RETURN m.lcXML

*/—————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: sp_user_login_id.PRG
* Purpose………: Проверка пароля и имени пользователя
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 22/05/2005
* Last edited…..: 06/05/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PROCEDURE sp_user_login_id
PARAMETERS m.User_nick, m.PASSWORD
SET DELETED ON
PRIVATE m.lnreturn_parameter
m.lnreturn_parameter=-1 && в случае неуспеха ответ в числовом виде
IF LEN(m.User_nick)>=3 AND LEN(m.PASSWORD)>3
SELECT User_ID, User_nick, admin, ISBLOCKED FROM USERS WHERE ;
UPPER(USERS.User_nick)==UPPER(m.User_nick) ;
AND UPPER(USERS.PASSWORD)==UPPER(m.PASSWORD) INTO CURSOR user_exists
IF RECCOUNT(‘USER_EXISTS’)>0 AND EMPTY(user_exists.ISBLOCKED)
* у нас есть такой пользователь
m.lnreturn_parameter=user_exists.User_ID
ENDIF
ENDIF
IF USED(‘USER_EXISTS’)
USE IN user_exists
ENDIF
IF USED(‘USERS’)
USE IN USERS
ENDIF
RETURN m.lnreturn_parameter

*/—————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: sp_users_read_xml.PRG
* Purpose………: Чтение списка пользователей с сервера
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 31/05/2005
* Last edited…..: 31/05/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PROCEDURE sp_users_read_xml
PARAMETERS m.lcUser_nick, m.lcpassword
SET DELETED ON
PRIVATE m.lnuser_id, lcXML
STORE 0 TO m.lnuser_id
m.lnuser_id=sp_user_login_id(m.lcUser_nick, m.lcpassword)
lcXML=-1 && в случае неуспеха ответ в числовом виде
IF m.lnuser_id>0
SELECT User_ID, User_nick FROM USERS ;
INTO CURSOR CUR_TEMP
IF RECCOUNT(‘CUR_TEMP’)>0
CURSORTOXML(«CUR_TEMP»,»lcXML»,1,1,0,»1″)
ENDIF
ENDIF
CLOSE TABLES ALL
RETURN lcXML

*/—————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: sp_new_message_add.PRG
* Purpose………: Добавление нового сообщения в таблицы Mes_Header, Mes_body
* а так-же обновляем информацию у клиента
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 30/05/2005
* Last edited…..: 04/06/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PROCEDURE sp_new_message_add
PARAMETERS m.User_nick, m.PASSWORD , m.lnUser_to, m.lcTITLE, m.lcMes_Body
SET DELETED ON
SET MULTILOCKS ON
PRIVATE m.lnreturn_parameter, m.lnuser_id, m.lnmes_id

m.lnreturn_parameter=-1
m.lnuser_id=sp_user_login_id(m.User_nick, m.PASSWORD)
IF m.lnuser_id>0
BEGIN TRANSACTION
TRY
INSERT INTO Mes_Header (User_from , User_to, WASUPDATED, TITLE, published) ;
VALUES ;
(m.lnuser_id,m.lnUser_to,DATETIME(),ALLTRIM(m.lcTITLE), DATETIME())
m.lnmes_id=GETAUTOINCVALUE(0)
INSERT INTO Mes_body (Mes_ID,User_from,MESSAGE,published ) ;
VALUES ;
(m.lnmes_id,m.lnuser_id, m.lcMes_Body, DATETIME())
UPDATE USERS SET lastvisit = DATETIME() WHERE User_ID=m.lnuser_id
END TRANSACTION
m.lnreturn_parameter=1
CATCH TO oException
ROLLBACK
FINALLY
ENDTRY
ENDIF
CLOSE TABLES ALL
RETURN m.lnreturn_parameter

*/—————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: sp_thread_read.PRG
* Purpose………: Чтение всего потока одного сообщения с сервера
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 04/06/2005
* Last edited…..: 06/06/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PROCEDURE sp_thread_read
PARAMETERS m.lcUser_nick, m.lcpassword, m.lnmes_id
SET DELETED ON
PRIVATE m.lnuser_id, m.lnadmin, lcXML
STORE 0 TO m.lnadmin, m.lnuser_id
m.lnuser_id=sp_user_login_id(m.lcUser_nick, m.lcpassword)
lcXML=-1 && в случае неуспеха ответ в числовом виде
IF m.lnuser_id>0
SELECT Mes_body.*,User_nick, city FROM Mes_body ;
LEFT OUTER JOIN USERS ON Mes_body.User_from=USERS.User_ID ;
WHERE Mes_ID=m.lnmes_id ;
INTO CURSOR CUR_TEMP &&
SELECT CUR_TEMP
IF RECCOUNT()>0
CURSORTOXML(«CUR_TEMP»,»lcXML»,1,1,0,»1″)
ENDIF
ENDIF
CLOSE TABLES ALL
RETURN lcXML

*/—————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: sp_existed_message_add.PRG
* Purpose………: Добавление нового сообщения в таблицу Mes_body
* а так-же обновляем информацию у клиента
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 05/05/2005
* Last edited…..: 06/06/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PROCEDURE sp_existed_message_add
PARAMETERS m.User_nick, m.PASSWORD , m.lcMes_Body, m.lnmes_id
SET DELETED ON
SET MULTILOCKS ON
PRIVATE m.lnreturn_parameter, m.lnuser_id
m.lnreturn_parameter=-1
m.lnuser_id=sp_user_login_id(m.User_nick, m.PASSWORD)
IF m.lnuser_id>0
BEGIN TRANSACTION
TRY
INSERT INTO Mes_body (Mes_ID,User_from,MESSAGE,published ) ;
VALUES ;
(m.lnmes_id,m.lnuser_id, m.lcMes_Body, DATETIME())
UPDATE USERS SET lastvisit = DATETIME() WHERE User_ID=m.lnuser_id
END TRANSACTION
m.lnreturn_parameter=1
CATCH TO oException
ROLLBACK
FINALLY
ENDTRY
ENDIF
CLOSE TABLES ALL
RETURN m.lnreturn_parameter

*/—————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: sp_message_delete.PRG
* Purpose………: Удаление всего потока сообщения с сервера
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 05/06/2005
* Last edited…..: 05/06/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PROCEDURE sp_message_delete
PARAMETERS m.lcUser_nick, m.lcpassword, m.lnmes_id
SET DELETED ON
SET MULTILOCKS ON
PRIVATE m.lnuser_id, m.lnadmin, lcXML
STORE 0 TO m.lnadmin, m.lnuser_id
m.lnuser_id=sp_user_login_id(m.lcUser_nick, m.lcpassword)
m.lnadmin=sp_chk_admin(m.lnuser_id)
m.lnreturn_parameter=-1 && в случае неуспеха ответ в числовом виде
IF m.lnuser_id>0 AND m.lnadmin>0
BEGIN TRANSACTION
TRY
DELETE FROM Mes_Header WHERE Mes_ID=m.lnmes_id
DELETE FROM Mes_body WHERE Mes_ID=m.lnmes_id
UPDATE USERS SET lastvisit = DATETIME() WHERE User_ID=m.lnuser_id
END TRANSACTION
m.lnreturn_parameter=1
CATCH TO oException
ROLLBACK
FINALLY
ENDTRY
ENDIF
CLOSE TABLES ALL
RETURN m.lnreturn_parameter

*/—————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: sp_answer_delete.PRG
* Purpose………: Удаление одного ответа из сообщения с сервера
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 05/06/2005
* Last edited…..: 05/06/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PROCEDURE sp_answer_delete
PARAMETERS m.lcUser_nick, m.lcpassword, m.lnReply_id, m.lnmes_id
SET DELETED ON
SET MULTILOCKS ON
PRIVATE m.lnuser_id, m.lnadmin, lcXML
STORE 0 TO m.lnadmin, m.lnuser_id
m.lnuser_id=sp_user_login_id(m.lcUser_nick, m.lcpassword)
m.lnadmin=sp_chk_admin(m.lnuser_id)
m.lnreturn_parameter=-1 && в случае неуспеха ответ в числовом виде
IF m.lnuser_id>0 AND m.lnadmin>0
BEGIN TRANSACTION
TRY
DELETE FROM Mes_body WHERE Reply_ID=m.lnReply_id
* теперь смотрим — остались ли еще сообщения и если нет — то удаляем
* и заголовок сообщения
SELECT COUNT(Mes_ID) AS RECORDS ;
FROM Mes_body ;
WHERE Mes_ID=m.lnmes_id ;
INTO CURSOR CUR_TEMP
SELECT CUR_TEMP
IF CUR_TEMP.RECORDS=0
DELETE FROM Mes_Header WHERE Mes_ID=m.lnmes_id
ENDIF
UPDATE USERS SET lastvisit = DATETIME() WHERE User_ID=m.lnuser_id
END TRANSACTION
m.lnreturn_parameter=1
CATCH TO oException
ROLLBACK
FINALLY
ENDTRY
ENDIF
CLOSE TABLES ALL
RETURN m.lnreturn_parameter

*/—————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: sp_users_profile_read_xml.PRG
* Purpose………: Чтение профиля пользователя с сервера
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 06/06/2005
* Last edited…..: 06/06/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PROCEDURE sp_users_profile_read_xml
PARAMETERS m.lcUser_nick, m.lcpassword, m.lnUserProfile_ID
SET DELETED ON
PRIVATE m.lnuser_id, lcXML, m.lnadmin
STORE 0 TO m.lnuser_id
m.lnuser_id=sp_user_login_id(m.lcUser_nick, m.lcpassword)
m.lnadmin=sp_chk_admin(m.lnuser_id)
lcXML=-1 && в случае неуспеха ответ в числовом виде
IF m.lnuser_id>0 AND m.lnadmin>0
SELECT * FROM USERS ;
INTO CURSOR CUR_TEMP WHERE User_ID=m.lnUserProfile_ID READWRITE NOFILTER
IF RECCOUNT(‘CUR_TEMP’)=0
SELECT CUR_TEMP
APPEND BLANK
ENDIF
CURSORTOXML(«CUR_TEMP»,»lcXML»,1,1,0,»1″)
ENDIF
CLOSE TABLES ALL
RETURN lcXML

*/—————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: sp_users_add_edit.PRG
* Purpose………: Добавление/изменение профиля клиента
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 06/06/2005
* Last edited…..: 06/06/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PROCEDURE sp_users_add_edit
PARAMETERS m.lcUser_nick, m.lcpassword, m.lnUserProfile_ID, m.lcXML_Cursor
SET DELETED ON
SET MULTILOCKS ON
PRIVATE m.lnuser_id, m.lnadmin, lcXML
STORE 0 TO m.lnadmin, m.lnuser_id
m.lnuser_id=sp_user_login_id(m.lcUser_nick, m.lcpassword)
m.lnadmin=sp_chk_admin(m.lnuser_id)
m.lnreturn_parameter=-1 && в случае неуспеха ответ в числовом виде
IF m.lnuser_id>0 AND m.lnadmin>0 AND !EMPTY(m.lcXML_Cursor)
XMLTOCURSOR(m.lcXML_Cursor,’TMCURSOR’,4)
SELECT TMCURSOR
IF RECCOUNT()>0 && у нас есть работа
IF m.lnUserProfile_ID>0 && обновление существующих данных
BEGIN TRANSACTION
TRY
* это конечно узкое место, но нам хотелось все сделать с помощью CRUD команд
UPDATE USERS SET User_nick=TMCURSOR.User_nick, PASSWORD = TMCURSOR.PASSWORD,;
email=TMCURSOR.email, city=TMCURSOR.city,admin = TMCURSOR.admin, ;
ISBLOCKED=TMCURSOR.ISBLOCKED ;
WHERE User_ID=m.lnUserProfile_ID
END TRANSACTION
m.lnreturn_parameter=1
CATCH TO oException
ROLLBACK
FINALLY
ENDTRY
ELSE && добавление нового клиента
BEGIN TRANSACTION
TRY
INSERT INTO USERS ;
(User_nick, PASSWORD , email, city, ;
admin , register, lastvisit, ISBLOCKED ) ;
VALUES ;
(TMCURSOR.User_nick,TMCURSOR.PASSWORD,TMCURSOR.email,TMCURSOR.city,;
TMCURSOR.admin, DATETIME(), DATETIME(),TMCURSOR.ISBLOCKED)
END TRANSACTION
m.lnreturn_parameter=1
CATCH TO oException
ROLLBACK
FINALLY
ENDTRY
ENDIF
ELSE && нечего делать
ENDIF
ENDIF
CLOSE TABLES ALL
RETURN m.lnreturn_parameter

Stored Procedures DBWS.DBC

Stored Procedures DBWS.DBC

Теперь, собственно, сам текст этой небольшой процедуры, которая создает эти ХП: */————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: Create_SP.PRG
* Purpose………: Создание Хранимых Процедур для базы данных DBWS.DBC
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 22/05/2005
* Last edited…..: 05/06/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PUBLIC m.pcpath
m.pcpath=’C:\WS_MESSAGE\SERVER\’
OPEN DATABASE (m.pcpath+’DBWS.DBC’)
APPEND PROCEDURES FROM (m.pcpath+’SP_PROCEDURES.TXT’) as 1251 OVERWRITE
CLOSE DATABASES

Create_SP.PRG

Кстати, в проекте, который Вы можете скачать ЗДЕСЬ (файл ws_mes_serv.zip 34 Kb) , так и сделано.
Второй путь — открыть базу данных, написать команду MODIFY PROCEDURE и в открывшееся окно скопировать текст всех хранимых процедур.
При отладке ХП могут иногда возникнуть проблемы — не отображаются внесенные Вами изменения. Для этого Вам надо открыть базу данных в режиме EXCLUSIVE и выполнить команду PACK DATABASE.
Теперь солидная «ложка дегтя» к нашей «бочке меда». Дело в том, что ряд разработчиков используют базы данных FoxPro и для других приложений, например, в ASP.NET. Работа с базами данных осуществляется с помощью Visual FoxPro OLE DB Provider, который, к сожалению, пока все еще не поддерживает структуру TRY..CATCH..ENDTRY. В этом случае нам придется обойтись старыми приемами, основанными на буферизации таблиц и вложенных транзакциях. Для простоты повествования приведем текст примера только для одной хранимой процедуры sp_message_delete(): */—————————————————————————/*
*
* MS VFP version..: 9.0
* Program-ID……: sp_message_delete.PRG
* Purpose………: Удаление всего потока сообщения с сервера
* Project Manager.:
* Programmer……: Sergey Chavlytko
* Start………..: 05/06/2005
* Last edited…..: 05/06/2005
*
* (С) www.sergey.co.uk 2005
*/—————————————————————————/*
PROCEDURE sp_message_delete
PARAMETERS m.lcUser_nick, m.lcpassword, m.lnmes_id
SET DELETED ON
SET MULTILOCKS ON
PRIVATE m.lnuser_id, m.lnadmin, lcXML
STORE 0 TO m.lnadmin, m.lnuser_id
m.lnuser_id=sp_user_login_id(m.lcUser_nick, m.lcpassword)
m.lnadmin=sp_chk_admin(m.lnuser_id)
m.lnreturn_parameter=-1 && в случае неуспеха ответ в числовом виде

CLOSE TABLES ALL
SELECT 0
USE MES_HEADER SHARED
=CURSORSETPROP(«Buffering» ,5,’MES_HEADER’ )
SELECT 0
USE MES_BODY SHARED
=CURSORSETPROP(«Buffering» ,5,’MES_BODY’ )
SELECT 0
USE USERS ALIAS USERS SHARED
=CURSORSETPROP(«Buffering» ,5,’USERS’ )
SELECT 0

IF m.lnuser_id>0 AND m.lnadmin>0

LOCAL llRollBack
llRollBack=.F.

BEGIN TRANSACTION
DELETE FROM MES_HEADER WHERE Mes_ID=m.lnmes_id
DELETE FROM MES_BODY WHERE Mes_ID=m.lnmes_id
UPDATE USERS SET lastvisit = DATETIME() WHERE User_ID=m.lnuser_id

IF !TABLEUPDATE(2,.T.,’MES_HEADER’)
llRollBack=.T.
ELSE
IF !TABLEUPDATE(2,.T.,’MES_BODY’)
llRollBack=.T.
ELSE
IF !TABLEUPDATE(2,.T.,’USERS’)
llRollBack=.T.
ENDIF
ENDIF
ENDIF
IF llRollBack
ROLLBACK
ELSE
END TRANSACTION
m.lnreturn_parameter=1
ENDIF

ENDIF
CLOSE TABLES ALL
RETURN m.lnreturn_parameter

Вариант ХП sp_message_delete() для Visual FoxPro OLE DB Provider

Код как обычно прозрачен, так что думаю, что можно опустить объяснения. В скачиваемом файле (ws_mes_serv.zip 34 Kb) процедура для создания Хранимых Процедур носит название CREATE_SP_OLEDB.PRG, а их тексты находятся в файле SP_PROCEDURES_OLEDB.TXT. Для примера с Web Service на основе FoxPro не имеет значения какие хранимые процедуры использовать, но Вам, как программисту FoxPro, должна быть понятна эта разница и ограничения, накладываемые Visual FoxPro OLE DB Provider.