-
2 MsGuns
г) в особо критичных ситуациях "гридного" редактирования не ленись использовать TClientDataSet со всеми его вкусностями (в т.ч. "тонкой" технологией управления кэшированием-отсылкой изменений) - ADO в сеточной режиме ведет себя иногда весьма капризно (особенно в конкурентных соединениях)
Я вот пытаюсь это сделать. С ClientDataSet раньше не работал.
Положил на одну форму:
ADODataSet1, DataSetProvider1 и ClientDataSet1.
Привязал их: ClientDataSet1.ProviderName := DataSetProvider1; DataSetProvider1.DataSet := ADODataSet1;
В ClientDataSet1 добавил persistent-поля в дизайнере с нужными именами и типами.
В ADODataSet1.CommandText вписал текст запроса детальной таблицы. Причем у меня там лежит JOIN двух таблиц (позиций заказов и справочника товаров)
Активизировал запрос. Активизировал ClientDataSet1.
В верхней сетке (та, что связана с ADODataSet1) данные редактируются и тут же посылаются на сервер. Я вижу в Studio Express, как изменения проявляются в базе. Этот ADO+MSSQL провайдеры настолько умны, что он умудряются не только редактировать позиции заказа (количества), но и изменять наименование товара каким-то фантастическим образом в справочнике товаров.
То есть, похоже, на лету разбираются с тем, какие поля из каких таблиц объединения JOIN я изменяю. Хотя в общем-то мне все это нафиг не нужно. Я хочу (пока это желание присутствует) ручками управлять ситуацией так, чтобы самому в нужный момент сформировать все нужные запросы.
Пойдем далее.
Итак, вижу в нижней сетке , подключенной к ClientDataSet1, набор данных.
При редактировании в этой сетке вижу, что исходный набор в верхней сетке не изменяется, ничего на сервер не посылается. Я безумно рад этому обстоятельству. Значит, я на верном пути.
Теперь я пытаюсь вызвать метод
ClientDataSet1.ApplyUpdates(0);
У меня возникли некоторые трудности. Возникает ошибка Invalid column name GOOD_NAME. Это как раз и есть поле справочника.
Все ли я в принципе делаю правильно? Можно ли перехватить событие обновления датасета? У меня не получилось (я пытался использовать OnWillChangeRecord и даже OnWillChangeField, но до этих событий, похоже, дело не доходит)
-
Похоже, моя задача проще решается без ClientDataSet. Методом ADODataSet1.UpdateBatch(). Хотя соображения выслушать всегда полезно.
-
> kaif ©
Тебе не нужет CDS, достаточно ADODataSet, как имеющего схожий функционал относительно локального кеширования.
-
Не представляя себе сути решаемой задачи, невозможно высказать какие-то соображения.
По поводу "умности" ADO. Есть куча статей в инете, где обсуждается именно эта особенность оболочки - "умение" разобраться в какой таблице что менять и глючность этого "умения". Эта путаница не в последнюю очередь вынудила меня отказаться от прямого гридного редактирования датасета. Хотя, вполне допускаю, что при достаточнной настырности можно заставить АДО работать корректно во всех случаях, я же предпочитаю обходиться другими, более прозрачными технологиями.
ЗЫ. ИМХО, ветка оформлена не по правилам и может быть удалена,
-
Я выбросил CDS. Оставил только ADODataSet. Но так и не смог избавиться от редактирования сразу всех таблиц, которое он мне устроил. :(
Пришлось реализовывать обновление в обход, в прямом смысле слова. Пожалуй ветку действительно можно удалить.
-
> kaif © (10.09.08 09:25) [4]
Там где-то в параметрах можно указать, какая таблица должна апдейтиться. Я не помню... sniknik подскажет.
-
-
2 sniknik © (10.09.08 10:37) [6]
Спасибо. Properties['Unique Table'].Value сработал. Но вот не могу задействовать
Update Criteria
Не знаю, что присвоить этому свойству. Мне нужно апдейтить только по первичному ключу. В статье говорится об adCriteriaKey, но такой константы в ADO нет.
-
Нет, показалось. К сожалению продолжает добавлять в таблицу справочника при инсерте. :( Зато константу adCriteriaKey нашел.
-
> Теперь я пытаюсь вызвать метод ClientDataSet1.ApplyUpdates(0);
в тяжелых случаях можно использовать Provider.BeforeUpdateRecord
-
Насколько я понял, ты не совсем по назначению используешь CDS ИМХО, есть 2 основных его назначения:
1) "Отложенные" изменения Датасет использует "прямое" подключение через провайдер, однако позволяет работать даже при потере соединения, сохраняя данные в кэше или даже на лок.диске. При возобновлении соединения делается попытка отправить изменения на сервер и перейти в "онлайн"
2) "Автономная" работа Датасет динамически создается по "образу и подобию" исходного. Туда "ручками" заливаются данные из исходного, исходный закрывается (в т.ч. включая само соединение). Вся работа ведется в CDS с периодическими (по кнопке или соотв.событиям) сохранениями на лок.диске. По кнопке данные датасета последовательно одиночными параметрическими запросами update-delete-insert) переносятся на сервер. Все выполняется в единой транзакции, запускаемой "ручками" через соединение (ADOConnection.BeginTrans), разумеется с проверками результата записи. Если Ок, цикл продолжается и по его завершении транзакция подтверждается, датасет переоткрывается "напрямую" и работа возобновляется как бы с начала. Если возникла ошибка записи-удаления, транзакция откатывается, причина отображется на экране, ошибочная запись становится текущей в датасете.
Этот способ, возможно, выглядит не совсем "красиво", но работает превосходно именно в конкурентных ситуациях, когда несколько пользователей обращаются к одному и тому же сложному объекту, хранящемуся в БД, и объект должен "выделяться" лишь одному из них.
-
В "автономном" режиме запросто реализуется сеточная правка в эксельной манере - при этом не загружается сервер, нет лишних проверок на целостность, исключена вероятность взаимовлияния пользователей друг на друга при модификациях одних и тех же объектов БД и скорость работы не зависит ни от каких факторов, кроме, конечно, производительности ПК клиента.
-
Еще в пользу CDS:
Он абсолютно не зависит от источника в плане сортировок, фильтров и т.д. В нем есть даже агрегаты, позволяющие делать объединения и итоги "как в экселе" (ну или почти как). Кроме того, полностью управляем в плане что и когда отправлять на сервер. Каждая запись у него имеет статус, который можно анализировать ДО отправки данных провайдеру. Единственный минус его (впрочем, вполне оборимый через добавление в uses Midas.pas или вроде того) - это неоходимость таскания Midas.dll, с которой могут быть проблемы (например, после установки на клиентский ПК какой-нибудь ранней версии Делфы)
-
2 MsGuns © Сергей, спасибо за столь подробное разъяснение. Я обязательно тщательно изучу работу с CDS в любом случае, чтобы осознанно выбирать наилучший вариант в любой ситуации. Так как работа у меня была тестовая и срочная, не столько на знание ADO, сколько нужно было быстро разобраться с MS SQL и обязательно при помощи стандартных компонентов Delphi6, то насколько это было возможно сделать в сжатые сроки, я пока остановился на решении: ADODataSet.LockType := ltBatchOptimistic ADODataSet.UpdateBatch() //по идее Причем сумел задействовать только чтение и кеширование данных для свободного редактирования "количеств накладной" в сетке. Обновления же я сделал пока совсем в лоб, обходом датасета в цикле и отправкой команд с помощью рантайм создаваемого TADOCommand. То есть в цикле создаю новые в во временной таблице, а затем одной командой удаляю все старые строки накладной, а новые - перекидываю в накладную при помощи insert into ... select from, а временную таблицу уничтожаю. Это очень грубое решение, но времени совершенствовать не было, так как у меня было много более важных задач в самой базе данных (поддержка остатков при помощи триггеров, быстрый отчет о движении за период и т.д.). Я долго бился (и сегодня вечером буду биться), пытаясь все же задействовать отправку методом ADODataSet.UpdateBatch(). Я надеюсь, что мне все же удастся заставить ADO отказаться пытаться вставлять в справочник значение-дубликат при попытке вставить лишь новую строку в накладную. Хотя меня этот момент уже бесит, если честно. Неужели разработчики самого ADO (или хотя бы дельфийской обертки) не могли сделать что-нибудь традиционное в виде 4 команд (SelectSQL, UpdateSQL, DeleteSQL, RefreshSQL), чтобы программист не мучился столько, пытаясь отговорить ADO делать то, что его вообще никто не просил... Моя мечта - перейти на компоненты прямого доступа и забыть это все в кошмарном сне. У меня сейчас новая засада (уже 5-я за трое суток). Хранимая процедура принимает 3 параметра 2: типа datetime, один типа varchar(50). Процедура строит отчет. В Studio Express отчет прекрасно работает. В дизайн-тайме в ADO при указании параметров в объект-инспекторе тоже работает. При попытке присвоить параметры рантайм, работает, но отчет выдает нулевые данные. Похоже, что параметры типа DateTime доходят криво. Я пытался обойтись без параметров вообще. Если явно присвоить строке CommandText в дизайнере: SP_MYPROC '20080101', '20081010', '%вася%'То потом рантайм методом Open отчет будет выведен правильно. Если же рантайм присвоить ту же самую строку, отчет выводит нули.
CommandText := 'SP_MYPROC ''20080101'', ''20081010'', ''%вася%''' Если бы мне кто-нибудь такое сказал, то я, пожалуй, действительно решил бы, что этот человек либо врет, либо у него с головой что-то не то.
-
У меня сейчас новая засада (уже 5-я за трое суток). Хранимая процедура принимает 3 параметра 2: типа datetime, один типа varchar(50). Процедура строит отчет. В Studio Express отчет прекрасно работает. В дизайн-тайме в ADO при указании параметров в объект-инспекторе тоже работает. При попытке присвоить параметры рантайм, работает, но отчет выдает нулевые данные. Похоже, что параметры типа DateTime доходят криво.
Я пытался обойтись без параметров вообще.
Если явно присвоить строке CommandText в дизайнере:
SP_MYPROC '20080101', '20081010', '%вася%'
То потом рантайм методом Open отчет будет выведен правильно.
Если же рантайм присвоить ту же самую строку, отчет выводит нули.
CommandText := 'SP_MYPROC ''20080101'', ''20081010'', ''%вася%'''
Если бы мне кто-нибудь такое сказал, то я, пожалуй, действительно решил бы, что этот человек либо врет, либо у него с головой что-то не то.
Прошу прощения. Это сообщение ошибочно.
-
Используй параметры, которые предварительно считывай из ХП (Parameters.Refresh)
|