|
|
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Добрый день! Ниже описан текст программы, который Вы мне как то предлагали использовать для сохранения изменений. Прошу поясните что за переменная m.gltransact, она не определена и в этом месте выдает ошибку. Спасибо за внимание. *___________________________________________ IF CURSORGETPROP("Buffering")=9999 && 3 LOCAL lnField,lcField && llOverwrite,llChanged, glCnanged=.F. glOverwrite=.F. FOR lnField=1 TO FCOUNT() lcField=FIELD(lnField) IF CURVAL(lcField)<>OLDVAL(lcField) glCnanged=.T. EXIT ENDIF ENDFOR IF glCnanged lnResult=MESSAGEBOX("База заблокирована другим пользователем, повторить попытку записи?",4,"Сохранение изменений...") IF lnResult=6 glOverwrite=.T. ENDIF ENDIF IF glCnanged AND NOT glOverwrite =TABLEREVERT() ELSE IF m.gltransact BEGIN TRANSACTION =TABLEUPDATE(.F.,.T.) END TRANSACTION ELSE =TABLEUPDATE(.F.,.T.) ENDIF ENDIF ENDIF LOCAL llSuccess, lcMessageText, laError(1), llOverWrite, llExit llOverWrite = .F. llExit = .F. DO WHILE llExit = .F. * Предполагаю что будет только один шаг цикла llExit = .T. BEGIN TRANSACTION llSuccess = TableUpdate(.T.,m.llOverWrite,alias()) IF llSuccess=.T. END TRANSACTION ELSE * Немедленный откат изменений ROLLBACK * Анализ причины ошибки ПОСЛЕ отката =AERROR(laError) IF laError[1,1]=1585 lcMessageText = "Пока Вы вносили изменения другим пользователем "+; "были изменены те же самые данные. Писать поверх внесенных изменений?" IF MessageBox(m.lcMessagetext,4+32+256,'Конфликт обновления')=6 * Ответили "Да". Повторяю цикл сохранения, но уже с перезаписью llExit = .F. llOverWrite = .T. loop ELSE * Ответили "Нет". Действия по обновлению данных ENDIF ELSE lcMessageText = "В процессе сохранения произошла ошибка № "+; LTRIM(STR(laError[1,1]))+chr(13)+laError[1,2] MessageBox(m.lcMessageText,0+48,'Ошибка при сохранении') ENDIF ENDIF ENDDO ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.12.2005, 16:37 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
А Вы ничего не путаете? То что написано после строки LOCAL llSuccess, lcMessageText, laError(1), llOverWrite, llExit Действительно похоже на код написанны мной, но вот первая часть IF CURSORGETPROP("Buffering")=9999 && 3 ... ENDIF какая-то уж слишком "дырявая". Много "провисших" кусков. Такое ощущение, что это выдрано откуда-то из более глобальной процедуры. Возможно, из цепочки триггеров. Тогда надо смотреть откуда этот код вызывается. Однако в любом случае, я не вижу смысла в том куске, который использует переменную m.gltransact. Похоже, что эта переменная проверяет факт существования (точнее, отсутствия) "внешней" транзакции (TXNLEVEL()). Но в любом случае нужен контроль корректности завершения функции TableUpdate(). В том виде, в котором приведен этот код, он не имеет смысла. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.12.2005, 17:32 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Вообщем задача такая, чтобы не морочиться с сохранением я в отдельной процедуре хочу сделать универсальную процедуру сохранения, Т.е. на всю комлексную программу где около 50 баз данных и множество не связанных задач есть общая кнопка "Редактировать" , "Сохранить" и "Изменить" и вот по нажатию этой кнопки сохранить- перебираются все 50 баз где скидываются буфера, может быть даже и в лишний раз. Прошу помогите с кодом этой процеДУРЫ, где в оптимальном варианте и безопасно можно скидывать с буфера в базу в одной процедуре. Примерно это будет так, как вы написали, а именно под мою задачу она сгодится? Спасибо зараннее! P.S. Я с этим месяц вожусь, то что предьявите мне - не глядя возьму за основу LOCAL llSuccess, lcMessageText, laError(1), llOverWrite, llExit llOverWrite = .F. llExit = .F. DO WHILE llExit = .F. * Предполагаю что будет только один шаг цикла llExit = .T. BEGIN TRANSACTION llSuccess = TableUpdate(.T.,m.llOverWrite,alias()) IF llSuccess=.T. END TRANSACTION ELSE * Немедленный откат изменений ROLLBACK * Анализ причины ошибки ПОСЛЕ отката =AERROR(laError) IF laError[1,1]=1585 lcMessageText = "Пока Вы вносили изменения другим пользователем "+; "были изменены те же самые данные. Писать поверх внесенных изменений?" IF MessageBox(m.lcMessagetext,4+32+256,'Конфликт обновления')=6 * Ответили "Да". Повторяю цикл сохранения, но уже с перезаписью llExit = .F. llOverWrite = .T. loop ELSE * Ответили "Нет". Действия по обновлению данных ENDIF ELSE lcMessageText = "В процессе сохранения произошла ошибка № "+; LTRIM(STR(laError[1,1]))+chr(13)+laError[1,2] MessageBox(m.lcMessageText,0+48,'Ошибка при сохранении') ENDIF ENDIF ENDDO ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.12.2005, 17:56 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
При написании универсальных процедур надо менять саму логику работы. Кроме того, для облегчения себе жизни (упрощения кода) следует ввести некоторые допущения. Сам процесс сохранения лично я (в своей задаче) разбиваю на несколько этапов: Этап 0 - проверка самого факта внесения каких-либо изменений Этап 1 - проверка факта ввода обязательных реквизитов и проверка корректности ввода данных Этап 2 - формирование значений служебных полей, недоступных пользователю и заполнение прочих полей, недоступных пользователю Этап 3 - собственно запись в базу данных Этап 4 - обновление информации на формах и во временных таблицах Каждый этап оформляется как отдельная процедура или метод класса. Более того, каждый этап, в свою очередь, разбивается на несколько подэтапов. В данном случае, интересен "Этап 3". Поскольку у меня любая модификация выполняется в буфферизированных таблицах, то сам процесс записи в базу данных (Этап 3) заключается в попытке сброса буфера. Тогда общая идеология такая: Код: plaintext 1. 2. 3. 4. В данном случае, я предполагаю, что происходит безусловная попытка сброса буфера. Т.е. в команде TableUpdate() второй параметр всегда .T. Если это не так, то дописать в этой схеме цикл - не проблема. В этом условном коде - 2 подэтапа (или 3, в зависимости от задачи) Первый подэтап - это собственно процесс сброса буфера. Оформляем как отдельную процедуру или метод класса. Например, ниже следующий код был написан на VFP6SP5. Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. Правда, здесь используется безусловный сброс буфера - второй параметр в команде TableUpdate() всегда .T., но его можно сделать еще одним параметром данной процедуры. Как видишь, здесь вообще нет разбора причины ошибки. Просто возвращает .T. или .F. А вот если этот метод вернул .F., то тогда делаем анализ причины ошибки. Для этого выполняем второй подэтап. Т.е. запускаем ЕЩЕ ОДИН метод, внутри которого вызывается функция AERROR() и выполняется разбор причины ошибки. Этот метод разбора причины ошибки может вести диалог с пользователем или только возвращать текст сообщения об ошибке. А уже в зависимости от причины можно либо повторно запустить метод сохранения, либо просто завершить основную процедуру сохранения. Я использовал такую схему анализа ошибки Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. В вызвавшей процедуре анализирую возвращенное значение и если это не пустая строка выдаю MessageBox(). Т.е. получаю примерно такой код Код: plaintext 1. 2. 3. 4. 5. Если оформить передачу списка полей в метод Save_Buffer как проперти класса, то можно совсем "отвязаться" от конкретной формы. Смысл всего выше сказанного в том, чтобы разбить процесс сохранения на несколько частей. Каждая часть решает свой круг задач. Такую схему достаточно легко модифицировать под свои нужды в конкретной ситуации. Т.е. можно вклинить дополнительную обработку между разными этапами. В случае единого универсального блока это становиться проблематичным. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 07.12.2005, 18:58 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Приветствую Владимир, большое спасибо за классный и комментированный код буду его использовать. Но возникла задача, которую вы предвидели, авторВ данном случае, я предполагаю, что происходит безусловная попытка сброса буфера. Т.е. в команде TableUpdate() второй параметр всегда .T. Если это не так, то дописать в этой схеме цикл - не проблема. НЕобходимо спрашивать юзера "Писать поверх изменений или нет" Вот только ума не приложу, где это "вставить", чтоб было так же красиво и понятно как у Вас :-) Ясно, что необходимо анализировать наличие ошибки AERROR(laError) IF laError[1,1]=1585 Но где и как???? То есть "отправить" юзера в метод * MessageError или прямо в методе SAVE_BUFFER ???? и тогда как и где??? Вообщем поимеем наглость, и для законченности этого красивого решения, смеем попросим Владимира дописать для нас неучей код под этот случай. Очень много людей будут Вам благодарны. Я не первый код в фоксе, но до сих пор так красиво писать не могу Спасибо ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 10.12.2005, 00:38 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Hi Владимир! Мне кажется что в общем случае не удастся сделать сохранение "всем скопом" - пример: Структура Master-Detail с Restrict триггерами - удалили данные из Master - удалили данные из Detail (всё в буфере конечно! при этом Detail 100% будет в table mode, а режим для Master не критичен - пускай это будет record mode, или table, но таблица в списке стоит ДО detail-таблицы) - выполняем сохранение по твоей схеме - на сохранении Master падаем по ошибке триггера - т.к. из detail данные ещё не удалены! они всё ещё в буфере. Ты скажешь - "значит надо поменять порядок сохранения, сначала сохраняя Detail, а потом Master" - хорошо, поменяем порядок, и рассмотрим вторую ситуацию - ввод новой записи - один новый Master и к нему несколько Detail - сначала сохраняем Detail - и конечно падаем по ошибке триггера :( Менять порядок обратно - тогда см. п.1. В общем я пришёл к выводу. что в подобной ситуации единственно приемлемым вариантом является МНОГОСТУПЕНЧАТЫЙ, ИЕРАРХИЧЕСКИЙ процесс сохранения данных. Начинаем сохранение с TableUpdate() для удалённых записей - при этом по всей иерархии связей курсоров идём от наиболее глубоких к корню (от Detail к Master-у). Дойдя до мастера (до корня), мы можем сохранить все записи в нём - а потом пойти сохранять изменённые и добавленные записи вниз по иерархии (но уже в обратном порядке - от более высоколежащих к более глубоким). Вариант изменения PK в Master таблицах я даже не рассматриваю, как заведомо бессмысленное действие, которое будет отметено Update триггером в любом случае. Posted via ActualForum NNTP Server 1.3 ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 10.12.2005, 03:50 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Да Владимир, можно ли еще Вас попросить привести код для случая с удалением записи, как в представлении, так и в таблице/группе таблиц, представлений. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 11.12.2005, 17:42 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Igor Korolyov Мне кажется что в общем случае не удастся сделать сохранение "всем скопом" - пример: Структура Master-Detail с Restrict триггерами - ... В общем случае, конечно. Но ведь я специально оговорился, что речь идет о конкретной (моей) задаче, где я же сам и ввел ряд ограничений и допущений. В частности, у меня невозможно одновременно (в одном буфере процесса редактирования) и создать и удалить записи из пары таблиц Master-Detail. Т.е. либо удаляешь, либо создаешь (модифицируешь), но никак не и то, и другое. Хотя в Detail допустимо и удалять и создавать в одном процессе. В таблицах, где идет связь один-ко-многим, триггер на удаление всегда Cascade, а на вставку всегда Restrict. Т.е. у меня (в моей задаче) такого вопроса вообще не стоит. Master всегда первая в списке на обновление, а за ней всегда Detail. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 11.12.2005, 22:12 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
~Иван~НЕобходимо спрашивать юзера "Писать поверх изменений или нет" Вот только ума не приложу, где это "вставить", чтоб было так же красиво и понятно как у Вас :-) Ясно, что необходимо анализировать наличие ошибки AERROR(laError) IF laError[1,1]=1585 Но где и как???? Ну, это достаточно просто. Идея заключается в том, что команда AERROR() не сбрасывает код ошибки. Т.е. ее можно запускать сколько угодно раз и она всегда будет возвращать одно и то же значение до тех пор, пока не возникнет новая ошибка. Т.е. можно примерно так: 1) В методе Save_Buffer добавлем 4 параметр, который будет определять значение второго параметра в команде TableUpdate(). Думаю, как это дописать в исходный код, сообразишь. 2) Собственно сохранение получается примерно так: Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. ~Иван~ Да Владимир, можно ли еще Вас попросить привести код для случая с удалением записи, как в представлении, так и в таблице/группе таблиц, представлений. Так ведь код не меняется. Нужно только будет следить за тем, в каком порядке выстраиваются имена алиасов представлений и таблиц при передаче списка параметров. У меня ведь, если дать команду Save_Buffer("Table1,Table2","Table3,Table4") Это означает, что сначала будет сброшен буфер текущей строки в Table1, затем буфер текущей строки в Table2, затем буфер всей таблицы в Table3 и затем буфер всей таблицы в Table4. Вот и остается определиться в каком порядке необходимо сбрасывать буферы Local View и таблиц-источников ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 11.12.2005, 22:39 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Приветствую всех, в частности Владимира Максимова Жажда универсализации сподвигла создавать меня глобальный объект для манипулирования записями В частности я работаю с представлениями в 5 буферизации, источником одного грида может быть несколько представлений, соединенных отношением 1-1 (потому что представление обновляет только одну таблицу) На основании кода Владимира, имел неосторожность кое-чего "дописать" Особенно меня интересует метод удаления записи. Подскажите какие грабли я не вижу, кроме вышеописанных Владимиром и Игорем спасибо Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. 174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. 189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203. 204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215. 216. 217. 218. 219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. 234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248. 249. 250. 251. 252. 253. 254. 255. 256. 257. 258. 259. 260. 261. 262. 263. 264. 265. 266. 267. 268. 269. 270. 271. 272. 273. 274. 275. 276. 277. 278. 279. 280. 281. 282. 283. 284. 285. 286. 287. 288. 289. 290. 291. 292. 293. 294. 295. 296. 297. 298. 299. 300. 301. 302. 303. 304. 305. 306. 307. 308. 309. 310. 311. 312. 313. 314. 315. 316. 317. 318. 319. 320. 321. 322. 323. 324. 325. 326. 327. 328. 329. 330. 331. 332. 333. 334. 335. 336. 337. 338. 339. 340. 341. 342. 343. 344. 345. 346. 347. 348. 349. 350. 351. 352. 353. 354. 355. 356. 357. 358. 359. 360. 361. 362. 363. 364. 365. 366. 367. 368. 369. 370. 371. 372. 373. 374. 375. 376. 377. 378. 379. 380. 381. 382. 383. 384. 385. 386. 387. 388. 389. 390. 391. 392. 393. 394. 395. 396. 397. 398. 399. 400. 401. 402. 403. 404. 405. 406. 407. 408. 409. 410. 411. 412. 413. 414. 415. 416. 417. 418. 419. 420. 421. 422. 423. 424. 425. 426. 427. 428. 429. 430. 431. 432. 433. 434. 435. 436. 437. 438. 439. 440. 441. 442. 443. 444. 445. 446. 447. 448. 449. 450. 451. 452. 453. 454. 455. 456. 457. 458. 459. 460. 461. 462. 463. 464. 465. 466. 467. 468. 469. 470. 471. 472. 473. 474. 475. 476. 477. 478. 479. 480. 481. 482. 483. 484. 485. 486. 487. 488. 489. 490. 491. 492. 493. 494. 495. 496. 497. 498. 499. 500. 501. 502. 503. 504. 505. 506. 507. 508. 509. 510. 511. 512. 513. 514. 515. 516. 517. 518. 519. 520. 521. 522. 523. 524. 525. 526. 527. 528. 529. 530. 531. 532. 533. 534. 535. 536. 537. 538. 539. 540. 541. 542. 543. 544. 545. 546. 547. 548. 549. 550. 551. 552. 553. 554. 555. 556. 557. 558. 559. 560. 561. 562. 563. 564. 565. 566. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.12.2005, 21:31 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Эх, красота-то какая! "мать, мать, мать...", по привычке откликнулось эхо Такие большие фрагменты кода лучше оформлять как файл, архивировать и класть во вложения. Собственно, даже мой фрагмент уже на "грани". Надо бы было тоже упаковать в архив. Твоя проблема в том, что ты пытаешся усидеть одновременно на 2 стульях. Точнее, даже на многих. Т.е. используешь несколько разных стилей программирования. С одной стороны, у тебя есть метод, формирующий сообщения об ошибке. Но с другой, ты формируешь это сообщение напрямую в коде. Т.е. как минимум, дублируешь код. С одной стороны, у тебя таблицы буферизированы. Но с другой, ты пытаешся позиционироваться в таблицах по физическому номеру записи. Для справки: новые записи в буфере таблицы получают отрицательный физический номер записи. Это некий предварительный номер, который после успешного сброса буфера будет заменен реальным положительным значением. Но это все не столь важно. Самая главная ошибка: Не надо пытаться писать универсальный код. Понимаешь, в одиночку просто невозможно сразу охватить все возможные проблемы. Да это и не нужно. Очень многое зависит от конкретной постановки задачи. Например, Игорь указал на ситуацию, когда мой код работать не будет. Но все дело в том, что в моей задаче такой ситуации просто не может возникнуть. Т.е. то или иное решение зависит от конкретной задачи. Кроме того, похоже ты не понимаешь идею классов. Т.е. когда создается класс, а на его основе класс-потомок, а на его основе еще один класс-потомок и т.д. и т.п. При такой иерархии классов в самом первом классе обычно код вообще не пишется. Этот класс родитель задает структуру обработки. Последовательность действий. Т.е. в нем формируются пустые методы, которые в классах-потомках будут реализовывать те самые описанные мной этапы сохранения и метод, который последовательно вызывает эти, пока пустые, методы. Почему в классах потомках? Ну, потому, что ситуации могут быть самые разные. В одном случае код сохранения должен быть одним, а в другом, чуть-чуть иначе. Разбиение на этапы (методы), как раз и служит для того, чтобы облегчить написание этого самого "чуть-чуть иначе". Т.е. чтобы не возникало необходимости копировать большие куски кода. Заранее оставляются некие "разрывы" в коде, куда и можно вписать свои отличия. Т.е. код должен быть не столько универсальным, сколько легко модифицируемым в классах-потомках. А метод, формирующий сообщение об ошибках вообще должен быть выделен в отдельный класс. Ошибки ведь могут возникать не только при сохранении. Зачем же "привязывать" этот метод именно к классу, занимающимся исключительно сохранением. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 12.12.2005, 23:52 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Hi Владимир! Что-то меня всё равно терзают смутные сомнения... А если в процессе работы сначала удалить И изменить одну или более записей в Detail (конечно разные записи - т.е. часть удалить, а часть изменить), а затем удалить саму эту Detail - разве при сохранении не будет проблем? Каскадный триггер то удалит записи из Detai ещё ДО того как пойдёт попытка сохранить данные из курсора Detail - и значит сохранить Detail не удастся... > Хотя в Detail допустимо и удалять и создавать в одном процессе. Вот это как раз и наводит на мысли... > В таблицах, где идет связь один-ко-многим, триггер на удаление всегда > Cascade А вот это очень жестоко. Связь такого типа не только в многострочных документах бывает - она ещё и между справочником и использующими его таблицами бывает - там каскадность IMHO недопустима. Или имеется в виду, что только для многострочных документов такие правила? А не для всех связей 1-M Posted via ActualForum NNTP Server 1.3 ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 13.12.2005, 03:21 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Игорь, ну не буду же я тут всю постановку задачи расписывать! Просто прими на веру, что у меня описанная ситуация исключена. Система достаточно жесткая и прямолинейная. Отсюда и такая простота кода. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 13.12.2005, 10:36 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
автор Твоя проблема в том, что ты пытаешся усидеть одновременно на 2 стульях. Точнее, даже на многих. Т.е. используешь несколько разных стилей программирования. С одной стороны, у тебя есть метод, формирующий сообщения об ошибке. Но с другой, ты формируешь это сообщение напрямую в коде. Т.е. как минимум, дублируешь код. Виноват, исправлюсь. Но... Владимир, Вы и сами в рекомендациях обработки ошибок рекомендовали мне следующее автор2) Собственно сохранение получается примерно так: LOCAL lcMessageText, laError(1), llOverWrite FOR lnI=1 TO 2 llOverWrite = (m.lnI=2) * По умолчанию, цикл выполняем только 1 раз lnI=2 * Выполняем попытку сохранения IF Save_Buffer("Table1,Table2","Table3,Table4",.T.,m.llOverWrite) = .F. * Сначала определяю номер ошибки =AERROR(laError) * Теперь формирую сообщение об ошибке lcMessageText = MessageError() * Если ошибка - это модификация другим пользователем, * то дополнительный запрос IF laError[1,1]=1585 lcMessageText = m.lcMessageText +chr(13)+; 'Писать поверх?' IF MessageBox(m.lcMessageText,4+32+256)=6 * Пошли на второй заход цикла m.lnI=1 ENDIF ELSE MessageBox(m.lcMessage) ENDIF ENDIF ENDFOR То есть тоже наблюдается "смешение", дублирование кода обработки ошибок. Это не препирательства :-) , а попытка научиться "писать как положено" Далее, как я уже гворил, аботаю с представлениями в 5 буферизации, модифицировал код метода saveBuffer следующим отразом авторIF m.tlTransaction=.T. IF m.llSuccess=.T. END TRANSACTION ELSE ROLLBACK FOR lnI=1 TO ALEN(laTableBuffer) *IF RECNO(laTableBuffer[m.lnI])<-1 TABLEREVERT(.T.,laTableBuffer[m.lnI]) &&åñëè íåîáõîäèìî *ENDIF ENDFOR ENDIF ENDIF то есть добавил откат изменений TABLEREVERT потому что возникает 1545 ошибка при реквери, но откат изменений во вьюхе при попытке сохранения Код: plaintext 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. соответственно откатывает изменения во вью после первого захода с проверкой "модификации другим пользователем" и мои изменения во вью сбрасываются в исходные, а надо чтоб остались Как побороть эту проблему и разрулить эту ситуацию. Как же меня интересует вопрос, следующего содержания: например сделали выборку-работаем, изменяем запись, сбрасываем буфер, а другой юзер в сети уже удалил эту запись возникает ошибка 1545, и соответствеено запись поверх к чему приведет???? %-) вообщем как эту ситуацию разрулить??? вообщем помогите победить у себя наследие самоучного 2.5 фокса спасибо ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 13.12.2005, 13:56 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Как мне кажется, твоя проблема в том, что ты слишком "зацикливаешся" на конкретной задаче. Т.е., несмотря на заявление о том, что "я хочу написать универсальный код", ты по прежнему, по сути, пишешь код для какой-то конкретной формы. Возьмем первый фрагмент, где ты модифицировал Save_Buffer. Зачем ты в этом коде делаешь откат по TableRevert()? Точнее, почему именно в этом методе? Цель метода Save_Buffer() - это выдать последовательно ряд команд TableUpdate(), по необходимости, окружив их транзакцией. ВСЕ! На этом его задача заканчивается. Он только сообщает удалось это сделать или нет. Если произошла неудача, то у тебя, как минимум, 2 сценария дальнейших действий: 1) Ошибка сохранения связана с некорректной модификацией или ошибка 1585. В этом случае откатывать ничего не надо. Пользователь либо должен скоррекировать свою модификацию, либо запустить тот же метод Save_Buffer() с другим параметром. 2) После получения сообщения об ошибке, пользователь решил вообще не сохранять внесенные изменения и закрыл форму. Если у тебя все таблицы в 5 режиме буферизации, то опять же не надо делать никаких откатов. Буфер и так будет удален без возражений. Т.е., в первом сценарии окат вообще не нужен, а во-втором - не обязателен. Ну, предположим, по каким-то соображениям, откат сделать необходимо. Но зачем это делать в той же самой процедуре? Ты, как минимум, уходишь от универсальности. Если это действие тебе необходимо, то оформи его как еще один метод, с именем, вроде Undo_Buffer(). Кстати, у себя я именно это и сделал. Я же уже приводил общую логику кода сохранения: Код: plaintext 1. 2. 3. 4. Обрати внимание, "возможный откат" вынесен во вне попытки сброса буфера. Т.е. это 2 разных метода. Вообще-то, мне просто непонятно, откуда у тебя появлась необходимость обновлять Local View в процессе сохранения данных ? Необходимо либо успешно сохранить все, либо откатить все. Причем это 2 разных процесса. Теперь, с сообщениями об ошибке. То, что я набросал, это было решение "на скорую руку". Вот потребовалось "заткнуть дырку", я и заткнул. По хорошему, надо писать не отдельный метод формирования текста сообщения, а целый класс по обработке ошибки. Ну, как минимум, в методе MessageError() делать запись выделенного кода ошибки в отдельную пропертю, чтобы не требовалось вызывать AERROR() повторно для уточнения кода ошибки. Почитай вот это http://www.foxhelp.ru/OshibkiObrabotkaVFP56DugJEnnig?v=6s0 Это первод стать Дуга Хеннинга о способах обработки ошибко в VFP6. А насчет, писать "поверх удаленной записи" есть несколько стратегий: 1) Если действительно "писать поверх", то запись будет восстановлена. Т.е. метка на удаление будет снята. Но! Это означает срабатывание триггера на вставку на данную запись. Там (в триггере) можно заблокировать (запретить) подобный процесс, если необходимо. Даже не обязательно специально отлавливать подобную ситуацию, просто, скорее всего, в триггер на вставку будут какие-то еще проверки, которые при восстановлении записи в подобной ситуации вызовут ошибку и, как следствие, отказ в сохранении. 2) Как правило, используют различные организационные способы "развести" пользователей так, чтобы вероятность редактирования одной и той же записи разными пользователями свести к минимуму. Прежде чем что-то писать, хорошо бы оценить, насколько предполагаемое событие вероятно и насколько тяжелы последствия от подобной ситуации. В моих задачах подобная ситуация крайне мало-вероятна, а последствия - незначительны. Т.е. просто нет смысла как-то специально это обрабатывать. Если у тебя, в твоих задачах это не так, то при возникновении ошибки 1585 сделай анализ Код: plaintext Эта функция "читает" данные напрямую с диска, а не из буфера. Т.е. по ее значению можно понять, была ли запись удалена и выдать соответствующее сообщение. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 14.12.2005, 23:09 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Hi Владимир! > В этом случае откатывать ничего не надо. Пользователь либо должен > скоррекировать свою модификацию, либо запустить тот же метод Save_Buffer() > с другим параметром. Вот чего чего, а редактировать буфер между 2-мя попытками его сброса совсем не стоит. Обсуждалась возникающая при этом ситуация "порчи" индекса этого курсора (к счастью только в памяти). > 1) Если действительно "писать поверх", то запись будет восстановлена. Это возможно исключительно при работе с таблицей непосредственно - ни локальные ни тем более удалённые представления такой "странной" функциональности не обеспечивают - там подобный конфликт "перебить" можно только заменив команду обновления с UPDATE на INSERT. > CurVal("Deleted()") Опять-же это полезно только при прямой работе с самой таблицей (и её буфером) - для случая представления/SPT запроса это неприменимо - там нужно отдельным запросом извлекать информацию о том. а что-же реально мы имеем в таблице на момент попытки сохранения... P.S. А напрямую с таблицами (пусть и буферизованными) я стараюсь не работать в интерфейсе. Posted via ActualForum NNTP Server 1.3 ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 15.12.2005, 02:14 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Igor KorolyovОпять-же это полезно только при прямой работе с самой таблицей (и её буфером) - для случая представления/SPT запроса это неприменимо - там нужно отдельным запросом извлекать информацию о том. а что-же реально мы имеем в таблице на момент попытки сохранения ... Игорь. Извлекать информацию куда? - на клиента - это тоже неправильный подход, ну извлекли, определили, что запись не удалена и что - это совсем не значит, что в момент команды UPDATE в таблице будет находится эта запись. Можно конечно попытаться использовать клиентскую транзакцию, НО - это тоже не выход. На мой взгляд, общее решение - это вызов ХП сервера, где надо производить разбор "что е, что нема", а клиенту вернуть только ошибку. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 15.12.2005, 10:05 |
|
||
|
Владимиру Максимову, вопрос
|
|||
|---|---|---|---|
|
#18+
Hi PaulWist! В общем случае - наверное так... Просто весьма утомительно руками писать подобные процедуры, а приемлемый генератор - поди отыщи (а свой писать тоже как-то ломает - тем более что сложно учесть все тонкости). Ну и врождённая проблема ХП - крайне сложно через неё менять ТОЛЬКО изменённые на клиенте поля - т.е. либо мы пихаем все поля (несмотря на то что на клиенте поменяли всего одно), либо удваиваем число парметров, строим внутри ХП нетривиальную логику (по сути создаём динамически команду обновления и исполняем её - это к тому-же наверняка не всякий SQL сервер позволит)... Posted via ActualForum NNTP Server 1.3 ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 16.12.2005, 02:03 |
|
||
|
|

start [/forum/topic.php?fid=41&msg=33431841&tid=1592771]: |
0ms |
get settings: |
7ms |
get forum list: |
24ms |
check forum access: |
4ms |
check topic access: |
4ms |
track hit: |
180ms |
get topic data: |
11ms |
get forum data: |
3ms |
get page messages: |
82ms |
get tp. blocked users: |
1ms |
| others: | 219ms |
| total: | 535ms |

| 0 / 0 |
