Конференция "Базы" » TClientDataSet + ADO + MSSQL [D6, MSSQL]
 
  • kaif © (10.09.08 00:50) [0]
    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, но до этих событий, похоже, дело не доходит)
  • kaif © (10.09.08 01:15) [1]
    Похоже, моя задача проще решается без ClientDataSet.
    Методом ADODataSet1.UpdateBatch().
    Хотя соображения выслушать всегда полезно.
  • Johnmen © (10.09.08 09:06) [2]

    > kaif ©

    Тебе не нужет CDS, достаточно ADODataSet, как имеющего схожий функционал относительно локального кеширования.
  • MsGuns © (10.09.08 09:14) [3]
    Не представляя себе сути решаемой задачи, невозможно высказать какие-то соображения.

    По поводу "умности" ADO. Есть куча статей в инете, где обсуждается именно эта особенность оболочки - "умение" разобраться в какой таблице что менять и глючность этого "умения".
    Эта путаница не в последнюю очередь вынудила меня отказаться от прямого гридного редактирования датасета. Хотя, вполне допускаю, что при достаточнной настырности можно заставить АДО работать корректно во всех случаях, я же предпочитаю обходиться другими, более прозрачными технологиями.

    ЗЫ. ИМХО, ветка оформлена не по правилам и может быть удалена,
  • kaif © (10.09.08 09:25) [4]
    Я выбросил CDS. Оставил только ADODataSet. Но так и не смог избавиться от редактирования сразу всех таблиц, которое он мне устроил. :(

    Пришлось реализовывать обновление в обход, в прямом смысле слова.
    Пожалуй ветку действительно можно удалить.
  • Johnmen © (10.09.08 09:29) [5]

    > kaif ©   (10.09.08 09:25) [4]

    Там где-то в параметрах можно указать, какая таблица должна апдейтиться.
    Я не помню... sniknik подскажет.
  • sniknik © (10.09.08 10:37) [6]
    > Там где-то в параметрах можно указать, какая таблица должна апдейтиться.
    Properties['Unique Table'].Value

    и вообще
    http://www.delphikingdom.com/asp/itemq.asp?Mode=1&ItemID=128
  • kaif © (10.09.08 11:00) [7]
    2 sniknik ©   (10.09.08 10:37) [6]

    Спасибо. Properties['Unique Table'].Value сработал.

    Но вот не могу задействовать

    Update Criteria

    Не знаю, что присвоить этому свойству. Мне нужно апдейтить только по первичному ключу. В статье говорится об adCriteriaKey, но такой константы в ADO нет.
  • kaif © (10.09.08 11:15) [8]
    Нет, показалось. К сожалению продолжает добавлять в таблицу справочника при инсерте. :(
    Зато константу adCriteriaKey нашел.
  • Правильный$Вася (10.09.08 11:55) [9]

    > Теперь я пытаюсь вызвать метод  ClientDataSet1.ApplyUpdates(0);

    в тяжелых случаях можно использовать Provider.BeforeUpdateRecord
  • MsGuns © (10.09.08 15:30) [10]
    Насколько я понял, ты не совсем по назначению используешь CDS
    ИМХО, есть 2 основных его назначения:

    1) "Отложенные" изменения
    Датасет использует "прямое" подключение через провайдер, однако позволяет работать даже при потере соединения, сохраняя данные в кэше или даже на лок.диске. При возобновлении соединения делается попытка отправить изменения на сервер и перейти в "онлайн"

    2) "Автономная" работа
    Датасет динамически создается по "образу и подобию" исходного. Туда "ручками" заливаются данные из исходного, исходный закрывается (в т.ч. включая само соединение). Вся работа ведется в CDS с периодическими (по кнопке или соотв.событиям) сохранениями на лок.диске. По кнопке данные датасета последовательно одиночными параметрическими запросами update-delete-insert) переносятся на сервер. Все выполняется в единой транзакции, запускаемой "ручками" через соединение (ADOConnection.BeginTrans), разумеется с проверками результата записи. Если Ок, цикл продолжается и по его завершении транзакция подтверждается, датасет переоткрывается "напрямую" и работа возобновляется как бы с начала. Если возникла ошибка записи-удаления, транзакция откатывается, причина отображется на экране, ошибочная запись становится текущей в датасете.

    Этот способ, возможно, выглядит не совсем "красиво", но работает превосходно именно в конкурентных ситуациях, когда несколько пользователей обращаются к одному и тому же сложному объекту, хранящемуся в БД, и объект должен "выделяться" лишь одному из них.
  • MsGuns © (10.09.08 15:35) [11]
    В "автономном" режиме запросто реализуется сеточная правка в эксельной манере - при этом не загружается сервер, нет лишних проверок на целостность, исключена вероятность взаимовлияния пользователей друг на друга при модификациях одних и тех же объектов БД и скорость работы не зависит ни от каких факторов, кроме, конечно, производительности ПК клиента.
  • MsGuns © (10.09.08 15:42) [12]
    Еще в пользу CDS:

    Он абсолютно не зависит от источника в плане сортировок, фильтров и т.д. В нем есть даже агрегаты, позволяющие делать объединения и итоги "как в экселе" (ну или почти как). Кроме того, полностью управляем в плане что и когда отправлять на сервер. Каждая запись у него имеет статус, который можно анализировать ДО отправки данных провайдеру.
    Единственный минус его (впрочем, вполне оборимый через добавление в uses Midas.pas или вроде того) - это неоходимость таскания Midas.dll, с которой могут быть проблемы (например, после установки на клиентский ПК какой-нибудь ранней версии Делфы)
  • kaif © (10.09.08 18:35) [13]
    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'', ''%вася%'''



    Если бы мне кто-нибудь такое сказал, то я, пожалуй, действительно решил бы, что этот человек либо врет, либо у него с головой что-то не то.
  • kaif © (10.09.08 21:13) [14]
    У меня сейчас новая засада (уже 5-я за трое суток).
    Хранимая процедура принимает 3 параметра 2: типа datetime, один типа varchar(50). Процедура строит отчет. В Studio Express отчет прекрасно работает. В дизайн-тайме в ADO при указании параметров в объект-инспекторе тоже работает.
    При попытке присвоить параметры рантайм, работает, но отчет выдает нулевые данные. Похоже, что параметры типа DateTime доходят криво.

    Я пытался обойтись без параметров вообще.

    Если явно присвоить строке CommandText в дизайнере:

    SP_MYPROC '20080101', '20081010', '%вася%'

    То потом рантайм методом Open отчет будет выведен правильно.

    Если же рантайм присвоить ту же самую строку, отчет выводит нули.

    CommandText := 'SP_MYPROC ''20080101'', ''20081010'', ''%вася%'''

    Если бы мне кто-нибудь такое сказал, то я, пожалуй, действительно решил бы, что этот человек либо врет, либо у него с головой что-то не то.


    Прошу прощения. Это сообщение ошибочно.
  • MsGuns © (10.09.08 21:37) [15]
    Используй параметры, которые предварительно считывай из ХП (Parameters.Refresh)
 
Конференция "Базы" » TClientDataSet + ADO + MSSQL [D6, MSSQL]
Есть новые Нет новых   [134435   +34][b:0][p:0.001]