Диспозить или не диспозить? Вот в чем вопрос...

Автор Тема: Диспозить или не диспозить? Вот в чем вопрос...  (Прочитано 3827 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Тема содержит сообщение с Решением. Нажмите здесь чтобы посмотреть его.

Оффлайн avcАвтор темы

  • ADN Club
  • *****
  • Сообщений: 805
  • Карма: 166
    • Мои плагины к Автокаду
Я тут поспорил с одним специалистом по поводу уничтожения (Dispose) объектов DBObject, которые созданы в моем коде с ноля и приписаны к транзакции (AddNewlyCreatedDBObject). Понятно, что если объект не будет уничтожен ни мной, ни транзакцией, то за него возьмется сборщик мусора из другого потока и может положить AutoCAD на лопатки. Я пока нигде такие объекты не уничтожаю, как и полученные из tr.GetObject. И вроде работает. Не без глюков, конечно. Но вот при отладке одного из клонов AutoCAD, я заметил в окне отладчика "Forgot to call Dispose?: DisposableWrapper". Это явно было про мой новый объект после диспоза транзакции. И я призадумался...
Вот тут help.autodesk.com/view/OARX/2021/ENU/?guid=GUID-9DFB5767-F8D6-4A88-87D6-9676C0189369 написано, что такие объекты надо непременно принудительно уничтожить. А вот тут www.keanw.com/2008/06/cleaning-up-aft.html Киан пишет прямо противоположное - как старые, так и новые объекты транзакция уничтожит сама. Правда Киан писал давно, может все изменилось. Но и официальной документации доверия тоже нет.
Если все-таки уничтожать - то как? Если объявить через using, то до tr.Commit объект в большинстве случаев недоживет (только в каких-то очень простых методах может дожить) и транзакция будет пытаться записать в базу данных удаленный объект... фатально? Если не использовать using, то надо писать что-то вроде
Код - C# [Выбрать]
  1. using (Transaction tr = db.TransactionManager.StartTransaction())
  2.  {
  3.       ....
  4.       tr.AddNewlyCreatedDBObject(newEntity, true); // где-то глубоко в циклах или вообще в другом методе с передачей tr в параметрах
  5.       ....
  6.       tr.Commit();
  7.       foreach (DBObject obj in tr.GetAllObjects())
  8.         if (!obj.IsDisposed)
  9.           obj.Dispose(); // то есть по идее удалять надо после комита, но до уничтожения самой транзакции
  10.   }
и так при каждом методе, где используется транзакция... дурдом? Зачем тогда вообще транзакции, если за ними все равно чистить надо.
Я проверил - GetAllObjects после Commit по прежнему выдает все объекты и они все еще не уничтоженные. Но там не только новые объекты, там все что я открывал и даже еще много чего, что открылось как-то косвенно. Лишний код, лишнее время... Или не лишнее...

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Вот тут help.autodesk.com/view/OARX/2021/ENU/?guid=GUID-9DFB5767-F8D6-4A88-87D6-9676C0189369 написано, что такие объекты надо непременно принудительно уничтожить.
Получается, что да, надо. И в примерах из той же справки все такие объекты диспозятся до Commit. Вот, например, создание отрезка: https://help.autodesk.com/view/OARX/2021/ENU/?guid=GUID-47E8A12E-2ED4-4E78-ADA3-AAC9B4223C3C . Блок using закрывается перед Commit.
Зачем тогда вообще транзакции, если за ними все равно чистить надо.
А вот это - правильный вопрос  :)

Оффлайн avcАвтор темы

  • ADN Club
  • *****
  • Сообщений: 805
  • Карма: 166
    • Мои плагины к Автокаду
И в примерах из той же справки все такие объекты диспозятся до Commit.
И совершенно не понятно как это может работать... И что будет, если повторно получить этот объект из транзакции? Я во все "мелкие" процедуры передаю транзакцию и есть большой шанс, что где-то кто-то этот объект  снова получит из tr.GetObject. там будет null ? или объект пересоздастся заново? откуда, если его еще нет в БД?

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Я во все "мелкие" процедуры передаю транзакцию
Проблемный сценарий. Я в итоге пришёл к тому, что во все "мелкие" методы передаю только ObjectId. А там внутри либо свою транзакцию запускаю, либо чаще без транзакции просто открываю объект. По выходу из "мелкого" метода объект закрывается.
Раньше передавал и открытые уже объекты, и транзакции запущенные. Но это большая головная боль за этим всем хозяйством потом следить, когда оно разрастается.

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
откуда, если его еще нет в БД?
В БД он уже есть, иначе у него не было бы Id. Там же порядок такой - сперва объект добавляется в БД, потом - в транзакцию, а потом только диспозится. В транзакцию невозможно добавить объект, если его нет в БД, если я всё правильно помню.

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Хех, что интересно - в старых гайдах нет такого требования, что нужно диспозить новые объекты. И примеры были без этого: http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%20.NET%20Developer%27s%20Guide/files/WS73099cc142f48755f2fc9df120970276f7205f.htm
Интересно, когда переделали? подозреваю, что не так уж давно

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Получается, что да, надо. И в примерах из той же справки все такие объекты диспозятся до Commit. Вот, например, создание отрезка: https://help.autodesk.com/view/OARX/2021/ENU/?guid=GUID-47E8A12E-2ED4-4E78-ADA3-AAC9B4223C3C . Блок using закрывается перед Commit.
Это не код, а полная ерунда. У меня даже слов нет. Дмитрий Загорулькин, мы же уже когда-то обсуждали это и даже смотрели исходники. Если использовать транзакции, то использовать using для объектов, которые можно помещать в базу (наследников DBObject) не следует, ибо если это объект уже добавлен в базу, то завершение using (т.н. метод Dispose) его закроет - фактически вызов метода Close, а если не добавлен, то уничтожит.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Дмитрий Загорулькин, мы же уже когда-то обсуждали это и даже смотрели исходники
Я что-то такое припоминаю, но к чему тогда пришли - уже не помню  :-[

Отмечено как Решение avc 08-04-2021, 21:08:38

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
avc,
Я тут поспорил с одним специалистом по поводу уничтожения (Dispose) объектов DBObject, которые созданы в моем коде с ноля и приписаны к транзакции (AddNewlyCreatedDBObject).
Их нужно самостоятельно диспозить только если у них ObjectId == null, т.е. они не добавлены в базу. Кстати, как сказано в документации, AddNewlyCreatedDBObject можно вызывать только для DBObject, которые уже добавлены в базу, т.е. у них ObjectId != null.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
avc,
После tr.Commit() все объекты, которыми управляла транзакция уже закрыты. Поэтому цикл
Код - C# [Выбрать]
  1.       foreach (DBObject obj in tr.GetAllObjects())
  2.         if (!obj.IsDisposed)
  3.           obj.Dispose();
в лучшем случае ничего делать не будет, а в худшем развалит AutoCAD, попытавшись закрыть уже закрытый объект.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн avcАвтор темы

  • ADN Club
  • *****
  • Сообщений: 805
  • Карма: 166
    • Мои плагины к Автокаду
Их нужно самостоятельно диспозить только если у них ObjectId == null, т.е. они не добавлены в базу.

Значит я был прав. А новая документация - нет  ;)
в лучшем случае ничего делать не будет, а в худшем развалит AutoCAD, попытавшись закрыть уже закрытый объект.
Цикл такой я попробовал - ничего он не делает, но и никого не разваливает, ни Автокад, ни прочих.
Это не код, а полная ерунда.
Однако так тоже работает - я тоже попробовал. И тоже ничего не развалилось. Но "прочим" это сильно помогло от фатала. Похоже кто-то слишком усердно читал документацию на API AutoCAD :)
В БД он уже есть, иначе у него не было бы Id. Там же порядок такой - сперва объект добавляется в БД, потом - в транзакцию, а потом только диспозится.
Действительно. Сам же сначала вставляю объект в BTR и получаю Id. И это вполне соответствует логике транзакций в СУБД - все запросы внутри транзакции должны видеть созданную запись. А с наружи - не должны видеть до Commit.
И что любопытно - можно спокойно менять свойства объекта, который уже записан в транзакцию, потом удалить его, потом вызвать Commit - все изменения окажутся в БД. То есть Commit вообще ничего не делает, все уже до него сохранено в БД прямо при изменении каждого свойства. Значит проблемы с удаленными объектами могут выскочить на tr.Abort() - возможно откатывать будет нечего. Надо будет попробовать...

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Цикл такой я попробовал - ничего он не делает, но и никого не разваливает, ни Автокад, ни прочих.
В одной из старых версий разваливал.
То есть Commit вообще ничего не делает, все уже до него сохранено в БД прямо при изменении каждого свойства.
Именно так. И именно поэтому Abort() работает значительно дольше Commit(), так как откатывает все изменения. Такова логика транзакций в AutoCAD. Кстати до AutoCAD 2006, в котором появился AutoCAD .NET API транзакциями мало кто пользовался (я про ObjectARX) - они не были популярны и мало известны. Это уже в AutoCAD .NET API их сделали основным средством работы с базой.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Кстати, как сказано в документации, AddNewlyCreatedDBObject можно вызывать только для DBObject, которые уже добавлены в базу
Я как-то на собственном опыте в этом убедился. Если объекта нет в базе, при попытке добавления его в транзакцию будет исключение с сообщением, что объект не был добавлен в базу.