![]() |
|
![]() ![]() ![]() |
|
Петрович |
|
||||||||||||||||||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1000 Регистрация: 2.12.2003 Где: Москва Репутация: 6 Всего: 55 |
Решил тут воспользоваться возможностью асинхронного исполнения запросов в ADO. Но, столкнулся с проблемой корректной обработки ошибок.
Для демонстрации, сделал простейший пример. 1. Бросил на форму компонент Memo1 для протоколирования происходящих событий 2. Бросил на форму компонент ADOConnection1 назначил ему два обработчика событий: OnBeforeConnect - для настройки соединения с демонстрационной БД поставляемой с Delphi:
OnExecuteComplete - для протоколирования в Memo1 результата выполнения запросов
3. Бросил на форму компонент Button1 и назначил ему обработчик события OnClick для отключения ADOConnection1 от БД:
4. Бросил на форму компонент Button2 и назначил ему обработчик события OnClick для запуска асинхронного исполнения запроса:
Не пугайтесь, реально, этот запрос ничего не удалит т.к. условие CustNo<>CustNo в принципе не выполнимо ![]() Теперь, запускаем проект и жмем Button2, дожидаемся появления в Memo1 строк:
После этого жмем Button1, и в итоге в Memo1 имеем:
Т.е., все как и предполагалось. А теперь, усложним ситуацию. Для этого добавим еще одну кнопку Button3 со следующим кодом обработчиком OnClick:
Обратите внимание на то что в исполняемом запросе идет обращение к несуществующей в БД таблице. Т.е., этот запрос не может быть исполнен без ошибки. А теперь, запустим проект и попытаемся выполнить этот ошибочный запрос нажатием Button3. В Memo1 получим:
Т.е., вроде все как положено. Но, интересное начинается потом. Теперь, нажмем кнопку Button1 для закрытия соединения. И, .... вместо нормального закрытия получаем исключение. Т.е. содержимое Memo1 станет таким:
Т.е., будет возбуждено исключение с результатом выполнения не заказанной операции (Close), а предыдущей асинхронной операции! При повторном нажатии на Button1, ADOConnection1 будет нормально закрыт. ИТОГО. Возникновение ошибки при асинхронном исполнернии SQL-оператора, ADO, запоминает это, и при первом следующем обращении возбуждает соответствующее исключение. Так вот вопрос в том, как сделать так что бы после обработки ошибки в OnExecuteComplete я не получал этого исключения при последующем обращении к ADO? P.S. Особенно забавно получается если в приведенном выше тесте после нажатия на Button3 закрыть программу не нажимая на Button1. В этом случае в программе возникают утечки памяти. Это показывает что разработчики Delphi сами не расчитывали на подобный ход событий. -------------------- Все знать невозможно, но хочется |
||||||||||||||||||
|
|||||||||||||||||||
SergeBS |
|
||||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1111 Регистрация: 10.6.2005 Где: Владимир Репутация: 11 Всего: 22 |
Петрович,
Например попробуй принудительно убить всю очередь команд:
Подозреваю, что все проще: пока у объекта не убита ошибочная ситуация - его не уничтожить. И пока не выполнена асинхронная операция - тоже. Попробуй просто длительную, но правильную операцию запустить на выполнение и закрыть приложение до ее завершения. Скорее всего утечка тоже будет. А потом она должна исчезнуть - по окончании операции. Если моя гипотеза верна. |
||||||
|
|||||||
Петрович |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1000 Регистрация: 2.12.2003 Где: Москва Репутация: 6 Всего: 55 |
Я не совсем понял куда ты предлагаешь это вставить? Я говорил о том, что уже после получения OnExecuteComplete с кодом (сообщением) ошибке, при попытке выполнить следующий, SQL-оператор я получу exception с докладом о предыдущем операторе. В принципе, решение я конечно нашел, но не считаю его правильным. Поэтому, опубликую позже, если не будет более корректных вариантов. -------------------- Все знать невозможно, но хочется |
|||
|
||||
SergeBS |
|
||||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1111 Регистрация: 10.6.2005 Где: Владимир Репутация: 11 Всего: 22 |
Петрович,
У тебя это:
Но проще - все в кучу, т.е. везде:
Т.е. при любом исключении - грохается вся очередь выполнения. Да, я это не проверял. Т.е. только предполагаю, что должно сработать. Дома - не забуду - попробую. |
||||
|
|||||
Петрович |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1000 Регистрация: 2.12.2003 Где: Москва Репутация: 6 Всего: 55 |
![]() Блин. Похоже я не очень ясно объясняю... Она и так грохается. О том что "она грохнулась" мне уже сообщили, передав в событие OnExecuteComplete даже сообщение об ошибке. Попробую пояснить еще раз, слегка изменив постановку вопроса. Работаю в асинхронном режиме. Выполняю ДВА опреатора. Естественно последовательно, т.е. второй оператор запускается на исполнение только после получения события OnExecuteComplete о завершении выполнения первого. Когда оба оператора нормально исполняются, то все хорошо. Но, если при исполнении ПЕРВОГО оператора возникла ошибка (о чем мне сообщают в параметрах EventStatus и Error события OnExecuteComplete, то потом, когда на исполнение запускается ВТОРОЙ оператор, возникает exception, с тем-же сообщением об ошибке (т.е. от первого оператора). Более того, можно даже не запускать второй SQL оператор, а просто например сказать Close для ADOConnection1, и в ответ получить исключение: ''Нет таблицы ....." Т.е., типа кто-то (один из модулей программы) неудачно выполнил запрос, а потом кто-то другой (другой модуль программы) в ответ на безобидный Close получает exception с совсем невразумительным сообщением! -------------------- Все знать невозможно, но хочется |
|||
|
||||
SergeBS |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1111 Регистрация: 10.6.2005 Где: Владимир Репутация: 11 Всего: 22 |
Петрович,
Я невнятно изложил. Предлагаемыми cancel и free очищается вся очередь команд на выполнение в ADOconnection. Т.е. и ошибочная - тоже. Нет команд - нет exception. Вообще-то calcel для тебя не нужно. Только для унификации - чтобы навесив на кнопку процедуру MyException можно было в любой момент прервать выполнение всего подряд. |
|||
|
||||
Петрович |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1000 Регистрация: 2.12.2003 Где: Москва Репутация: 6 Всего: 55 |
Да я понимаю что такое очистка очереди команд!
Но, во первых, в момент возникновения события OnExecuteComplete никакой очереди уже не существует - запрос исполнен (удачно или не удачно)! Во вторых, туда, куда ты предлагаешь вставить код мы вообще никогда не попадаем! Речь идет об асинхронном исполнении запросов. Т.е., когда делается:
исключения не возникает - запрос лишь отправляется на исполнение, и еще не известно что он содержит ошибку. Это сообщение отредактировал(а) Петрович - 2.10.2007, 11:41 -------------------- Все знать невозможно, но хочется |
|||
|
||||
Петрович |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1000 Регистрация: 2.12.2003 Где: Москва Репутация: 6 Всего: 55 |
Ха!
Тут на всякий случай все же заглянул на то что такое свойство Commands которым мне тут предлагали воспользоваться.... Выяснилось, к обсуждаемой теме оно вообще не имеет никакого отношения!!!! В общем, я так понял, среди посетителей форума нет программеров использующих асинхронное исполнение запросов ![]() В общем, буду использовать пока найденное мною решение (которое мне не нравится). Оно заключается в том, что бы в обработчике OnExecuteComplete, если получен код завершения с ошибкой, вызывать обращение к объекту ADO, с игнорированием exception. Т.е. получается что-то вроде такого:
Тогда, если Execute завершился неудачей, то чтение статуса вызовет exception который будет благополучно проглочен. Однако, столкнулся с еще одной проблемкой ![]() Если вызвать ADOConnection1.Close сразу из обработчика ADOConnection1ExecuteComplete то программа вообще "зависает" наглухо! Т.е. управление уходит в библиотеку ADO и из нее уже не возвращается! В общем, бардак да и только. ![]() Хотя, весь мой жизненный опыт подсказывает что это все таки наверное я что-то делаю некорректно. Может кто-то хоть посоветует куда заглянуть за примерами работы в асинхронном режиме с ADO ? -------------------- Все знать невозможно, но хочется |
|||
|
||||
kobra |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 730 Регистрация: 15.6.2005 Где: Грузия, Тбилиси Репутация: 1 Всего: 9 |
Петрович, не а если посмотреть что в это врема передоется к базе и обратно? имею в виду просмотреть в профаилере.
почему то складывется впечетление что после неудачного завершения, при следуиших деиствиах (в данном случае перед закритием) ADOConnection пытается снова выполнить старыи запрос (чтоб не остатся в долгу).
вопрос правда интересный, но делфиа нет, и сам проверить не могу. |
|||
|
||||
Петрович |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1000 Регистрация: 2.12.2003 Где: Москва Репутация: 6 Всего: 55 |
Угу. А ему не кажется что он за меня что-то решает? Такого быть не должно. Т.к. получив от него ошибку я уже мог предпринять некоторые ответные действия (например подготовить к запуску ядерную ракету ![]() И вообще, у нормальной программы мозги могут в трубочку свернуться когда на обращение к свойству State, она получает исключение с кодом и сообщением "Ядро базы данных Microsoft Jet не может найти входную таблицу или запрос 'НесуществующаяТаблица'. Проверьте существование таблицы или запроса и правильность имени." А если серьезно, то трассировка по коду модулей дельфи приводит к тому что я вижу что при обращении к COM-объекту вываливается ошибка. Что еще трассировать я не знаю ![]() Кроме того, в рабочем проекте, ошибку у меня вызывает запрос требующий экслюзивного доступа к БД. Собственно она и возникает потому что БД открыта другим процессом. Так вот, пред тем как она возникает, ADO ожидает некоторое время (типа вдруг освободят). Соответственно, реальная попытка выполнить занимает около 15-сек. А вот при втором обращении (после обнаружения), exception возникает практически мгновенно.
В принципе я его понимаю. Однако, не все так просто. Если даже сделать так:
В результате, все равно получаю висяк ![]() Нормально срабатывает если делаю так:
Вот так. Похоже все таки есть некоторая специфика использования асинхронного режима. Только реально я не смог нигде найти описания подобных тонкостей. И вообще, что-то мне не удалось найти реальных примеров использования асинхронного исполнения запросов. Хотя, теперь у меня таки получился код асинхронно выполняющий SQL-скрипт ![]() Но, пока выбора нет. -------------------- Все знать невозможно, но хочется |
|||
|
||||
kobra |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 730 Регистрация: 15.6.2005 Где: Грузия, Тбилиси Репутация: 1 Всего: 9 |
потому я и просил посмотреть профаилер (к mssql подклучи). т.е. трасировать не в делфи а обращение к базе. если второи раз сообшение вылетает моментально, наверняка обращение к базе уже не происходит. значит при первой неудавшеися попытке нужно сделать чтото, что коректно освободит какоито ресурс, или сбросит флаг, или . . .. но какои, даже не догадывюс.
правда мне очен странным кажется вот что если ты уверен что обработчик OnExecuteComplete после отправки Post-а завершается, то может попробуеш такои вариант. 1. В обрабочике OnExecuteComplete я Post'ом посылаю своему главному окну специальное сообщение и завершаю обработчик. 2. В обработчике этого специального сообщения (т.е. ужа за пределами OnExecuteComplete), запустить таимер, хота бы на пару секунд. 3. таимер вызывает Close. или же прямо из OnExecuteComplete запустить таимер. но в лубом случае, проблема останется, так как проблема реално состоит в правилной обработке исклучения. |
|||
|
||||
Петрович |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1000 Регистрация: 2.12.2003 Где: Москва Репутация: 6 Всего: 55 |
Пробовал - нормально. И это понятно. Срабатывание таймера, это правтически адекватно моему второму сообщению. Более того, я почти таким способом и "нащупал" способ со вторым сообщением.
Не, проблема "необходимости двойного сообщения" никак не связана с ошибкой исполнения запроса. Она проявляется даже если все асинхронные запросы выполнинилсь без ошибок. ![]() -------------------- Все знать невозможно, но хочется |
|||
|
||||
kobra |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 730 Регистрация: 15.6.2005 Где: Грузия, Тбилиси Репутация: 1 Всего: 9 |
||||
|
||||
vinvin |
|
|||
Новичок Профиль Группа: Участник Сообщений: 35 Регистрация: 30.1.2007 Репутация: нет Всего: 1 |
А зачем непременно связываться с асинхронными запросами?
Ну если не работает или непонятно как работает - можно обойти проблему с помощью создания потока (TThread). Создай поток, закинь на исполнение в нем команды в синхронной обработке, там же и обработку ошибок. И проблем, как мне кажется, будет меньше, да и решение проще. |
|||
|
||||
Петрович |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1000 Регистрация: 2.12.2003 Где: Москва Репутация: 6 Всего: 55 |
Вопрос спорный. В сложном приложении, решение проблем синхронизации потоков может свести на нет всю "кажущуюся" простоту такого решения. В простых приложениях, действительно это может быть решением - хотя тоже еще надо проверить как ADO себя ведет при одновременном доступе из нескольких потоков. ![]() -------------------- Все знать невозможно, но хочется |
|||
|
||||
![]() ![]() ![]() |
Правила форума "Delphi: Базы данных и репортинг" | |
|
Запрещено: 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами Обязательно указание: 1. Базы данных (Paradox, Oracle и т.п.) 2. Способа доступа (ADO, BDE и т.д.)
FAQ раздела лежит здесь! Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Vit, Петрович. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Delphi: Базы данных и репортинг | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |