Объект не закрывется при фиксации транзакции

Автор Тема: Объект не закрывется при фиксации транзакции  (Прочитано 9026 раз)

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

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

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

  • ADN OPEN
  • Сообщений: 17
  • Карма: 1
Объект открывается через механизм транзакций дважды в разных режимах. Транзакции вложенные. Для некоторых сочетаний режимов открытия объекта внешняя транзакция при фиксации не закрывает объект.

Это я что-то делаю неправильно или это баг Автокада? (Проверял на 2015 и на 2017.)

Ниже код функции, демонстрирующей эффект.

Сначала создаю объект AcDbLine и вставляю его в базу. Затем под первой транзакцией открываю объект в режиме AcDb::kForWrite, а под второй - в режиме AcDb::kForNotify и закрываю обе транзакции с фиксацией. После этого пытаюсь удалить объект.

Функция выводит в лог:
ErrorStatus = 83
--- Read enabled
--- Write enabled

Т.е. при попытке открыть объект на запись возвращается eWasOpenForWrite (код 83), а дальнейшее копание через указатель на объект показывает, что он открыт и на запись, и на чтение. (Конечно возможно, что объект по указтелю уже попорчен, поскольку объект вроде должен быть закрыт при закрытии внешней транзакции. Но если это так, вообще непонятно как выкручиваться.) После явного вызова close() объект наконец закрывается и вторая попытка открыть объект на запись с последующим удалением проходит успешно. И это, с учётом того, что у меня есть какой-никакой указатель на объект. А если весь этот демонстрационный код раскидан по разным функциям/методам/классам, как это есть в реальности?


Код - C++ [Выбрать]
  1. void Test_Trans() {
  2.  
  3.         AcDbObjectId id;
  4.         AcDbObject *pObj1 = nullptr, *pObj2 = nullptr;
  5.         Acad::ErrorStatus es;
  6.  
  7.         // создать объект AcDbLine с фиксированными координатами начала и конца
  8.         AcDbDatabase *pCurDb = acdbHostApplicationServices()->workingDatabase();
  9.         AcDbBlockTable *pBlockTable = nullptr;
  10.         pCurDb->getSymbolTable(pBlockTable, AcDb::kForRead);
  11.         AcDbBlockTableRecord *pModelSpace = nullptr;
  12.         pBlockTable->getAt(ACDB_MODEL_SPACE, pModelSpace, AcDb::kForWrite);
  13.         pBlockTable->close();
  14.         AcGePoint3d startPt(0.0, 0.0, 0.0), endPt(2000.0, 1000.0, 0.0);
  15.         AcDbLine *pLine = new AcDbLine(startPt, endPt);
  16.         pModelSpace->appendAcDbEntity(id, pLine);
  17.         pLine->close();
  18.         pModelSpace->close();
  19.  
  20.         // открытие объекта через транзакции
  21.         AcTransaction *pTrans1 = actrTransactionManager->startTransaction();
  22.  
  23.         es = pTrans1->getObject(pObj1, id, AcDb::kForWrite, false);
  24.         if (es != Acad::eOk)
  25.                 acutPrintf(L"pTrans1->getObject() ErrorStatus = %d\n", (int)es);
  26.  
  27.         AcTransaction *pTrans2 = actrTransactionManager->startTransaction();
  28.  
  29.         es = pTrans2->getObject(pObj2, id, AcDb::kForNotify, false);
  30.         if (es != Acad::eOk)
  31.                 acutPrintf(L"pTrans2->getObject() ErrorStatus = %d\n", (int)es);
  32.  
  33.         actrTransactionManager->endTransaction();
  34.         actrTransactionManager->endTransaction();
  35.  
  36.         AcDbEntity *pEnt = NULL;
  37.         // первая попытка прибить объект
  38.         es = acdbOpenObject(pEnt, id, AcDb::OpenMode::kForWrite);
  39.         if (pEnt && es == Acad::eOk) {
  40.                 pEnt->erase(Adesk::kTrue);
  41.                 pEnt->close();
  42.         }
  43.         else {
  44.                 acutPrintf(L"ErrorStatus = %d\n", (int)es);
  45.  
  46.                 if (pObj1 && id.isResident()) {
  47.                         bool needFix = false;
  48.                         if (pObj1->isReadEnabled()) {
  49.                                 acutPrintf(L"--- Read enabled\n");
  50.                                 needFix = true;
  51.                         }
  52.                         if (pObj1->isWriteEnabled()) {
  53.                                 acutPrintf(L"--- Write enabled\n");
  54.                                 needFix = true;
  55.                         }
  56.                         if (pObj1->isNotifyEnabled()) {
  57.                                 acutPrintf(L"--- Notify enabled\n");
  58.                                 needFix = true;
  59.                         }
  60.                         if (needFix)
  61.                                 pObj1->close();
  62.  
  63.                         // вторая попытка прибить объект
  64.                         es = acdbOpenObject(pEnt, id, AcDb::OpenMode::kForWrite);
  65.                         if (pEnt && es == Acad::eOk) {
  66.                                 pEnt->erase(Adesk::kTrue);
  67.                                 pEnt->close();
  68.                         }
  69.                         else
  70.                                 acutPrintf(L"ErrorStatus = %d\n", (int)es);
  71.                 }
  72.         }
  73. }
  74.  

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Это я что-то делаю неправильно или это баг Автокада? (Проверял на 2015 и на 2017.)
Это не баг. Причина в том, что actrTransactionManager->endTransaction() не приводит к немедленному закрытию вовлеченных в транзакцию объектов в отличие от pObj->close(). Почитай в документации: https://knowledge.autodesk.com/search-result/caas/CloudHelp/cloudhelp/2017/ENU/OARXMAC-DevGuide/files/GUID-F33EB1D7-6C92-4A77-A061-2001B4A390BA-htm.html
И вообще смешение двух вариантов работы с объектами (с транзакцией и без) чревато очень неприятными ошибками.
Я предпочитаю вообще не пользоваться транзакциями.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN OPEN
  • Сообщений: 17
  • Карма: 1
Т.е. actrTransactionManager->endTransaction() работает асинхронно. Тогда, исходя из ваших слов и
Цитировать
You can modify individual objects after each has been committed, but it is recommended that you cache the IDs of the objects you want to modify and wait until you receive the transactionEnded() notification signaling the end of all the transactions, then do the modifications.
я не совсем понимаю, как использовать транзакции в синхронном коде, как в моём примере.

Получается, что в районе строки 35 должна быть какая-то задержка до момента окончания закрытия транзакции. Вызов actrTransactionManager->numActiveTransactions() сразу после закрытия внешней транзакции возвращает 0, следовательно его использовать нельзя. Исходя из вышеприведённой цитаты из документации, надо делать отдельный класс, унаследованный от AcTransactionReactor, переопределять в нём transactionEnded(), цеплять к менеджеру транзакций. А в строке 35 в цикле ждать, пока не отработает реактор и не выставит флаг, что транзакция полностью завершилась. Так? Или есть способ попроще?

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Так? Или есть способ попроще?
Попроще только не использовать транзакции (тем более вложенные).
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN OPEN
  • Сообщений: 17
  • Карма: 1
Попроще только не использовать транзакции (тем более вложенные).
Тоже не от меня зависит.
Спасибо за ответы.

Отмечено как Решение lom0v 26-03-2019, 19:24:04

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
lom0v,
Я немного подрихтовал твой код с использованием интелектуальных указателей (2 -го вида):
Код - C++ [Выбрать]
  1. static void MyGroupTestTrans() {
  2.   AcDbObjectId id;
  3.   AcDbObject *pObj1 = nullptr, *pObj2 = nullptr;
  4.   Acad::ErrorStatus es;
  5.  
  6.   // создать объект AcDbLine с фиксированными координатами начала и конца
  7.   AcDbDatabase *pCurDb = acdbHostApplicationServices()->workingDatabase();
  8.   AcDbBlockTable *pBlockTable = nullptr;
  9.   pCurDb->getSymbolTable(pBlockTable, AcDb::kForRead);
  10.   AcDbBlockTableRecord *pModelSpace = nullptr;
  11.   pBlockTable->getAt(ACDB_MODEL_SPACE, pModelSpace, AcDb::kForWrite);
  12.   pBlockTable->close();
  13.   AcGePoint3d startPt(0.0, 0.0, 0.0), endPt(2000.0, 1000.0, 0.0);
  14.   AcDbLine *pLine = new AcDbLine(startPt, endPt);
  15.   pModelSpace->appendAcDbEntity(id, pLine);
  16.   pLine->close();
  17.   pModelSpace->close();
  18.  
  19.   // открытие объекта через транзакции
  20.   AcTransaction *pTrans1 = actrTransactionManager->startTransaction();
  21.  
  22.   es = pTrans1->getObject(pObj1, id, AcDb::kForWrite, false);
  23.   if (es != Acad::eOk)
  24.     acutPrintf(L"pTrans1->getObject() ErrorStatus = %s\n", acadErrorStatusText(es));
  25.  
  26.   AcTransaction *pTrans2 = actrTransactionManager->startTransaction();
  27.  
  28.   es = pTrans2->getObject(pObj2, id, AcDb::kForNotify, false);
  29.   if (es != Acad::eOk)
  30.     acutPrintf(L"pTrans2->getObject() ErrorStatus = %s\n", acadErrorStatusText(es));
  31.  
  32.   es = actrTransactionManager->endTransaction();
  33.   es = actrTransactionManager->endTransaction();
  34.  
  35.   //////////////////////////////////////////////////////////////////////////
  36.   //  Используем новые интеллектуальные указатели
  37.   //////////////////////////////////////////////////////////////////////////
  38.   AcDbSmartObjectPointer<AcDbEntity> pEnt(id, AcDb::OpenMode::kForWrite);
  39.   if ((es = pEnt.openStatus()) == Acad::eOk)
  40.   {
  41.     pEnt->erase(Adesk::kTrue);
  42.   }
  43.   else
  44.   {
  45.     acutPrintf(L"pEnt.openStatus() ErrorStatus = %s\n", acadErrorStatusText(es));
  46.   }
  47. }

И судя по всему всё нормально работает.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN OPEN
  • Сообщений: 17
  • Карма: 1
Да, с этим работает. Спасибо!