Сообщество программистов Autodesk в СНГ

ADN Club => AutoCAD .NET API => Тема начата: avc от 08-04-2021, 18:02:51

Название: Диспозить или не диспозить? Вот в чем вопрос...
Отправлено: avc от 08-04-2021, 18:02:51
Я тут поспорил с одним специалистом по поводу уничтожения (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 (http://help.autodesk.com/view/OARX/2021/ENU/?guid=GUID-9DFB5767-F8D6-4A88-87D6-9676C0189369) написано, что такие объекты надо непременно принудительно уничтожить. А вот тут www.keanw.com/2008/06/cleaning-up-aft.html (http://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 по прежнему выдает все объекты и они все еще не уничтоженные. Но там не только новые объекты, там все что я открывал и даже еще много чего, что открылось как-то косвенно. Лишний код, лишнее время... Или не лишнее...
Название: Re: Диспозить или не диспозить? Вот в чем вопрос...
Отправлено: Дмитрий Загорулькин от 08-04-2021, 18:28:51
Вот тут 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.
Зачем тогда вообще транзакции, если за ними все равно чистить надо.
А вот это - правильный вопрос  :)
Название: Re: Диспозить или не диспозить? Вот в чем вопрос...
Отправлено: avc от 08-04-2021, 18:44:31
И в примерах из той же справки все такие объекты диспозятся до Commit.
И совершенно не понятно как это может работать... И что будет, если повторно получить этот объект из транзакции? Я во все "мелкие" процедуры передаю транзакцию и есть большой шанс, что где-то кто-то этот объект  снова получит из tr.GetObject. там будет null ? или объект пересоздастся заново? откуда, если его еще нет в БД?
Название: Re: Диспозить или не диспозить? Вот в чем вопрос...
Отправлено: Дмитрий Загорулькин от 08-04-2021, 19:38:07
Я во все "мелкие" процедуры передаю транзакцию
Проблемный сценарий. Я в итоге пришёл к тому, что во все "мелкие" методы передаю только ObjectId. А там внутри либо свою транзакцию запускаю, либо чаще без транзакции просто открываю объект. По выходу из "мелкого" метода объект закрывается.
Раньше передавал и открытые уже объекты, и транзакции запущенные. Но это большая головная боль за этим всем хозяйством потом следить, когда оно разрастается.
Название: Re: Диспозить или не диспозить? Вот в чем вопрос...
Отправлено: Дмитрий Загорулькин от 08-04-2021, 19:41:40
откуда, если его еще нет в БД?
В БД он уже есть, иначе у него не было бы Id. Там же порядок такой - сперва объект добавляется в БД, потом - в транзакцию, а потом только диспозится. В транзакцию невозможно добавить объект, если его нет в БД, если я всё правильно помню.
Название: Re: Диспозить или не диспозить? Вот в чем вопрос...
Отправлено: Дмитрий Загорулькин от 08-04-2021, 20:00:20
Хех, что интересно - в старых гайдах нет такого требования, что нужно диспозить новые объекты. И примеры были без этого: http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%20.NET%20Developer%27s%20Guide/files/WS73099cc142f48755f2fc9df120970276f7205f.htm
Интересно, когда переделали? подозреваю, что не так уж давно
Название: Re: Диспозить или не диспозить? Вот в чем вопрос...
Отправлено: Александр Ривилис от 08-04-2021, 20:13:20
Получается, что да, надо. И в примерах из той же справки все такие объекты диспозятся до Commit. Вот, например, создание отрезка: https://help.autodesk.com/view/OARX/2021/ENU/?guid=GUID-47E8A12E-2ED4-4E78-ADA3-AAC9B4223C3C . Блок using закрывается перед Commit.
Это не код, а полная ерунда. У меня даже слов нет. Дмитрий Загорулькин, мы же уже когда-то обсуждали это и даже смотрели исходники. Если использовать транзакции, то использовать using для объектов, которые можно помещать в базу (наследников DBObject) не следует, ибо если это объект уже добавлен в базу, то завершение using (т.н. метод Dispose) его закроет - фактически вызов метода Close, а если не добавлен, то уничтожит.
Название: Re: Диспозить или не диспозить? Вот в чем вопрос...
Отправлено: Дмитрий Загорулькин от 08-04-2021, 20:18:04
Дмитрий Загорулькин, мы же уже когда-то обсуждали это и даже смотрели исходники
Я что-то такое припоминаю, но к чему тогда пришли - уже не помню  :-[
Название: Re: Диспозить или не диспозить? Вот в чем вопрос...
Отправлено: Александр Ривилис от 08-04-2021, 20:20:06
avc,
Я тут поспорил с одним специалистом по поводу уничтожения (Dispose) объектов DBObject, которые созданы в моем коде с ноля и приписаны к транзакции (AddNewlyCreatedDBObject).
Их нужно самостоятельно диспозить только если у них ObjectId == null, т.е. они не добавлены в базу. Кстати, как сказано в документации, AddNewlyCreatedDBObject можно вызывать только для DBObject, которые уже добавлены в базу, т.е. у них ObjectId != null.
Название: Re: Диспозить или не диспозить? Вот в чем вопрос...
Отправлено: Александр Ривилис от 08-04-2021, 20:26:17
avc,
После tr.Commit() все объекты, которыми управляла транзакция уже закрыты. Поэтому цикл
Код - C# [Выбрать]
  1.       foreach (DBObject obj in tr.GetAllObjects())
  2.         if (!obj.IsDisposed)
  3.           obj.Dispose();
в лучшем случае ничего делать не будет, а в худшем развалит AutoCAD, попытавшись закрыть уже закрытый объект.
Название: Re: Диспозить или не диспозить? Вот в чем вопрос...
Отправлено: avc от 08-04-2021, 21:37:21
Их нужно самостоятельно диспозить только если у них ObjectId == null, т.е. они не добавлены в базу.

Значит я был прав. А новая документация - нет  ;)
в лучшем случае ничего делать не будет, а в худшем развалит AutoCAD, попытавшись закрыть уже закрытый объект.
Цикл такой я попробовал - ничего он не делает, но и никого не разваливает, ни Автокад, ни прочих.
Это не код, а полная ерунда.
Однако так тоже работает - я тоже попробовал. И тоже ничего не развалилось. Но "прочим" это сильно помогло от фатала. Похоже кто-то слишком усердно читал документацию на API AutoCAD :)
В БД он уже есть, иначе у него не было бы Id. Там же порядок такой - сперва объект добавляется в БД, потом - в транзакцию, а потом только диспозится.
Действительно. Сам же сначала вставляю объект в BTR и получаю Id. И это вполне соответствует логике транзакций в СУБД - все запросы внутри транзакции должны видеть созданную запись. А с наружи - не должны видеть до Commit.
И что любопытно - можно спокойно менять свойства объекта, который уже записан в транзакцию, потом удалить его, потом вызвать Commit - все изменения окажутся в БД. То есть Commit вообще ничего не делает, все уже до него сохранено в БД прямо при изменении каждого свойства. Значит проблемы с удаленными объектами могут выскочить на tr.Abort() - возможно откатывать будет нечего. Надо будет попробовать...
Название: Re: Диспозить или не диспозить? Вот в чем вопрос...
Отправлено: Александр Ривилис от 08-04-2021, 22:31:25
Цикл такой я попробовал - ничего он не делает, но и никого не разваливает, ни Автокад, ни прочих.
В одной из старых версий разваливал.
То есть Commit вообще ничего не делает, все уже до него сохранено в БД прямо при изменении каждого свойства.
Именно так. И именно поэтому Abort() работает значительно дольше Commit(), так как откатывает все изменения. Такова логика транзакций в AutoCAD. Кстати до AutoCAD 2006, в котором появился AutoCAD .NET API транзакциями мало кто пользовался (я про ObjectARX) - они не были популярны и мало известны. Это уже в AutoCAD .NET API их сделали основным средством работы с базой.
Название: Re: Диспозить или не диспозить? Вот в чем вопрос...
Отправлено: Дмитрий Загорулькин от 09-04-2021, 09:46:05
Кстати, как сказано в документации, AddNewlyCreatedDBObject можно вызывать только для DBObject, которые уже добавлены в базу
Я как-то на собственном опыте в этом убедился. Если объекта нет в базе, при попытке добавления его в транзакцию будет исключение с сообщением, что объект не был добавлен в базу.