Можно ли работать с Entity получив из функции

Автор Тема: Можно ли работать с Entity получив из функции  (Прочитано 13718 раз)

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

Оффлайн Алексей (IdeaSoft)Автор темы

  • ADN
  • *
  • Сообщений: 1188
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
У меня есть функция, которая возвращает Entity по ObjectID
Вот такой код
Код - vb.net [Выбрать]
  1.     Public Shared Function acadnet_Entity_GetByID(ByVal ID As CAD_DBS.ObjectId, _
  2.                                                   ByVal db As CAD_DBS.Database) As CAD_DBS.Entity
  3.         Dim ent As CAD_DBS.Entity = Nothing
  4.         Using trans As CAD_DBS.Transaction = db.TransactionManager.StartTransaction
  5.             Try
  6.                 Dim dbObj = trans.GetObject(ID, CAD_DBS.OpenMode.ForRead, False, True)
  7.                 ent = DirectCast(dbObj, CAD_DBS.Entity)
  8.             Catch ex As System.Exception
  9.             End Try
  10.             trans.Commit()
  11.         End Using
  12.         Return ent
  13.     End Function

К примеру я буду использовать эту функцию в подпрограмме TEST
с условием, что полученный объект будет использоваться только для чтения
(мне нужно получать разные его поля в зависимости от типа примитива)
Вот так:

Код - vb.net [Выбрать]
  1.     Public Shared Sub TEST()
  2.         Dim id As CAD_DBS.ObjectId
  3.         Dim db As CAD_DBS.Database
  4.         '...
  5.         ' Инициализация id и db
  6.         '...
  7.         Dim getEnt As CAD_DBS.Entity = acadnet_Entity_GetByID(id, db)
  8.         If getEnt IsNot Nothing Then
  9.             ' объект getEnt буду использовать только для чтения данных его полей
  10.         End If
  11.     End Sub

У меня есть подозрение, что если программировать в таком стиле, то есть вероятность ошибок,
т.к. не контролирую состояние полученного объекта getEnt.
Я думаю, что лучше так не делать.
 
Хочу услышать мнения экспертов?
 
 




Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 809
  • Карма: 166
    • Мои плагины к Автокаду
Присоединяюсь к вопросу.
Я бы не стал закрывать транзакцию. Ее лучше открыть и коммитить уровнем выше. Тогда точно проблем не будет. Ну или создать клон объекта. Клон не привязан ни к БД, ни к транзакции - храните сколько хотите.
А в вашем коде trans.Commit наверно вызовет Entity.Dispose. Так?

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 809
  • Карма: 166
    • Мои плагины к Автокаду
И еще в вашем случае незачем передавать в функцию db. У ObjectId и так есть Database. А у Datebase есть TransactionManager.TopTransaction. Так что хратит одного параметра ObjectId.

Оффлайн Андрей Бушман

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Это называется работой через зад (ИМХО). Время жизни об'екта, полученного через транзакцию, регулируется этой транзакций. Транзакции нужно корректно завершать, дабы освобождать ресурсы. Иначе ты будешь ловить тотал эрроры.

У меня в блоге есть инфа по работе с транзакцией и без.

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
А в вашем коде trans.Commit наверно вызовет Entity.Dispose. Так?
А Entity.Dispose в свою очередь вызовет Entity.Close и ent станет неопределённой. Так что рубить сук, на котором сидишь, не советую.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Алексей (IdeaSoft)Автор темы

  • ADN
  • *
  • Сообщений: 1188
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
Ребята! Спасибо большое за ответы.

Оффлайн Алексей (IdeaSoft)Автор темы

  • ADN
  • *
  • Сообщений: 1188
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
как удалить этот пустой ответ?

Оффлайн Алексей (IdeaSoft)Автор темы

  • ADN
  • *
  • Сообщений: 1188
  • Карма: 9
    • idea-soft.ru
  • Skype: makar_govorun
Клон не привязан ни к БД,
Да о клоне. (Entity.Clone)
Я попробовал сделать клона для Entity и действительно можно получать информацию из полей клона, но не всю.

Такие поля как Handle ObjectID у клона = 0.
Ну это и понятно. т.к. клон он же не привязан к БД.

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 809
  • Карма: 166
    • Мои плагины к Автокаду
да у клона есть некоторые ограничения. Как и у нового объекта. Например не сработает FullSubEntityPath у солидов. Но есть и куча достоинств - можно безболезненно и безопасно делать с ним все что душе угодно: например подвинуть, повернуть, чтоб легче было обмерить, сравнить с другими объектами. И никаких фаталов, даже если нечаянно забыть Dispose().
ObjectId (вместе с Handle) придется сохранять отдельно.
Вообще, если придерживаться принципа "Один CommandMethod - одна транзакция" (т.е. открывать транзакцию сразу в начале обработчика команды и закрывать в самом конце, и во все функции передавать ЭТУ же транзакцию через TopTransaction или через параметр функции), то тогда таких проблем вообще возникнуть не должно.

Оффлайн Андрей Бушман

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Чем меньше зависимостей, тем надёжней код. Самый надёжный способ (имхо) - передавать методу в качестве параметра ObjectId, без каких-либо транзакций и прочей шелухи.

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 809
  • Карма: 166
    • Мои плагины к Автокаду
Насчет зависимостей - да, конечно. Но про транзакцию - нееее! Тут, как обычно надо баланс соблюдать. Открывать новую транзакцию (если она вообще нужна) по каждому чиху, в каждой процедурке из 3х строк? А если процедурка эта будет вызываться в цикле по всех БД чертежа? Это ж катастрофа. Да и суть транзакции, что б сделать откат всего CommandMethod. А если коммитить на каждом шагу весь смысл теряется. (тоже ИМХО)

Оффлайн Андрей Бушман

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Открывать новую транзакцию (если она вообще нужна) по каждому чиху, в каждой процедурке из 3х строк? А если процедурка эта будет вызываться в цикле по всех БД чертежа? Это ж катастрофа.
А ты пробовал реально замерять разницу в скорости?

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 809
  • Карма: 166
    • Мои плагины к Автокаду
Не-а  ::) Верю знатокам на слово  :D А что, не тормозит на самом деле? Попробовать что ли....

Оффлайн Андрей Бушман

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Не-а  ::) Верю знатокам на слово  :D А что, не тормозит на самом деле? Попробовать что ли....
Мой вопрос был без подвоха. Попробуй, конкретно это я не замерял (измерял др. вещи). Вряд ли падение производительности будет существенным, тем более, что в реальности код твоего метода вряд ли будет состоять из "трёх строчек". Кроме того, получить объект и работать с ним можно в т.ч. и без транзакции. Что быстрее в той или иной ситуации - это нужно замерять на конкретных примерах.

Оффлайн Андрей Бушман

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Сервер тупит. Александр Наумович, удалите продублированное сообщение. Регулярно получаю сообщения о том, что сервер вышел покурить...

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 809
  • Карма: 166
    • Мои плагины к Автокаду
Ну что ж. Проверил. Тестовая функция открывает объект по Id и возвращает его Layer (вполне себе полезная функциональность в 3 строки). Тестовый черетж из первых попавшихся под руку плоских объектов - 448шт.
Открываем объект на чтение.
    В одной транзакции - 0.009сек, в новых транзакциях - 0.214сек
Открываем объект на запись.
    В одной транзакции - 0.023сек, в новых транзакциях - 0.272сек
Открываем объект на запись и абортим каждую транзакцию.
    В одной транзакции - 0.012сек (странно!), в новых транзакциях - 1.713сек

Может и не катастрофа, но если так делать на каждом шагу - пользователь не обрадуется.

Оффлайн Андрей Бушман

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
вполне себе полезная функциональность в 3 строки
А без транзакций? Кроме того, не видя твоего кода сложно судить о том, насколько оптимально ты выполняешь те или иные операции.

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 809
  • Карма: 166
    • Мои плагины к Автокаду
А без транзакций я не умею. У меня ObjectId.GetObject выдает невнятный эксцепшн
А код и смотреть нечего:
Код - C# [Выбрать]
  1.     private static string _getLayer(ObjectId id)
  2.     {
  3.       string ret;
  4.       Document doc = AcadApp.DocumentManager.MdiActiveDocument;
  5.       using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
  6.       {
  7.         Entity en = tr.GetObject(id, OpenMode.ForRead) as Entity;
  8.         ret = en.Layer;
  9.         tr.Commit();
  10.       }
  11.       return ret;
  12.     }

Оффлайн Андрей Бушман

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
А без транзакций я не умею. У меня ObjectId.GetObject выдает невнятный эксцепшн
http://bushman-andrey.blogspot.ru/2013/01/database-autocad.html

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Document doc = AcadApp.DocumentManager.MdiActiveDocument;
      using (Transaction tr = doc.Database
Зачем? Есть же id.Database

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 809
  • Карма: 166
    • Мои плагины к Автокаду

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 809
  • Карма: 166
    • Мои плагины к Автокаду
Вот так не работает:
Код - C# [Выбрать]
  1.     [CommandMethod("VT", CommandFlags.Redraw)]
  2.     public static void VT()
  3.     {
  4.       try
  5.       {
  6.         Document doc = AcadApp.DocumentManager.MdiActiveDocument;
  7.         Editor ed = doc.Editor;
  8.         // получить предвыделенные объекты или запросить ввод
  9.         ObjectId[] sel = CnsAcad.Select("Выберите объекты для проверки", false, true);
  10.         if (sel == null) return;
  11. // первый способ
  12.         DateTime n = DateTime.Now;
  13.         string ret = "";
  14.         int counter = 0;
  15.         using (doc.LockDocument())
  16.         using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
  17.         {
  18.           foreach (ObjectId id in sel)
  19.           {
  20.             Entity en = tr.GetObject(id, OpenMode.ForRead) as Entity;
  21.             ret += en.Layer; // просто так. для теста
  22.             counter++;
  23.           }
  24.           tr.Commit();
  25.           }
  26.         ed.WriteMessage("Счетчик: " + counter.ToString() + " время: " + (DateTime.Now - n).TotalSeconds.ToString());
  27.  
  28. // второй способ
  29.         n = DateTime.Now;
  30.         ret = "";
  31.         counter = 0;
  32.         using (doc.LockDocument()) // можно убрать в данном случае. ни на что не влияет
  33.         foreach (ObjectId id in sel)
  34.         {
  35.           ret += _getLayer(id);
  36.           counter++;
  37.         }
  38.         ed.WriteMessage("Счетчик: " + counter.ToString() + " время: " + (DateTime.Now - n).TotalSeconds.ToString());
  39.       }
  40.       catch (System.Exception ex) { ed.WriteMessage(ex.Message); }
  41.     }
  42.  
  43.     private static string _getLayer(ObjectId id)
  44.     {
  45.       using (Entity en = id.GetObject(OpenMode.ForRead) as Entity) // вот тут стабильно "Object reference not set to an instance of an object."
  46.         return en.Layer;
  47.     }
  48.  

Заметил, что я DateTime.Now вызывал до селекта объектов а это лишние 5мс. так что разница времени еще больше

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
А если, все же, прочитать статью Андрея? Ссылка в сообщении #19.
Еще можно мое творение глянуть:

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 809
  • Карма: 166
    • Мои плагины к Автокаду
Перечитал статью в 6ой раз. Заметил наконец в чем прикол - вместо GetObject используется Open.
Теперь не сбоит. Хотя где-то я точно видел, что использует народ именно ObjectId.GetObject... Не сам же я такое придумал :)

Итого для открытия только на чтение:
Одна транзакция - не более 0.01сек
448 транзакций с коммитом - 0.34сек
Без транзакций вообще - 0.29сек

Вывод: для массовых процедур транзакцию надо открывать и надо передавать во все функции. Вооот

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Хотя где-то я точно видел, что использует народ именно ObjectId.GetObject... Не сам же я такое придумал
Это как раз, чтобы транзакцию не передавать во вспомогательные методы. Смотрите видео, в общем. Неохота по 100 раз одно и то же повторять :)

Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 809
  • Карма: 166
    • Мои плагины к Автокаду
Ну а разница между явными транзакциями и скрытыми не столь велика, что б на нее ориентироваться. Меня слегка напрягает, что Open помечен 'ObjectId.Open(OpenMode)" является устаревшим: 'For advanced use only. Use GetObject instead'. Есть же какая-то причина, что его так пометили


Оффлайн avc

  • ADN Club
  • *****
  • Сообщений: 809
  • Карма: 166
    • Мои плагины к Автокаду
Неохота по 100 раз одно и то же повторять
Самое обидное, что я это знал, читал, но напроч забыл, т.к. ни разу не использовал. Ну не удержать все в голове, извиняйте...

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Так можно еще по форуму поискать. Эта тема уже довольно избита. И Александр Наумович и Андрей Андреевич здесь уже несколько раз на подобные вопросы отвечали.

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
А без транзакций я не умею. У меня ObjectId.GetObject выдает невнятный эксцепшн
Это тоже самое, что с транзакцией, но используется TopTransaction. Если ни одна транзакция не запущена - то само-собой возникнет исключение.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Меня слегка напрягает, что Open помечен 'ObjectId.Open(OpenMode)" является устаревшим: 'For advanced use only. Use GetObject instead'. Есть же какая-то причина, что его так пометили
Разницу между устаревшим и "For advanced use only" чувствуешь? Если на каждый Open не будет Close - гарантированный Fatal Error. Поэтому в большистве случаев используется конструкция типа
Код - C# [Выбрать]
  1. using (Entity en = id.Open(...) as Entity)
  2. {
  3.   // Здесь работаем с en
  4. }
en.Dispose при завершении using автоматом вызывает en.Close
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Если на каждый Open не будет Close - гарантированный Fatal Error.
Помимо этого, есть еще 2 проблемы, которые могут возникнуть при использовании Open:
1. Если объект ранее был открыт на запись, или на чтение 255 раз - исключение. Вы как-то сами мне это подсказали ;). Вот как проверить это перед открытием объекта? У транзакции нет таких ограничений. Я так понимаю, что она не переоткрывает объект, а находит его среди открытых и возвращает ссылку на него.
2. А что будет в Вашем примере, если открытый объект не удастся привести к Entity? Объект откроется, но не закроется, как я понимаю.

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
1. Если объект ранее был открыт на запись, или на чтение 255 раз - исключение. Вы как-то сами мне это подсказали ;). Вот как проверить это перед открытием объекта?
Если использовать using, то никогда в такую ситуацию не попадёшь. Разве что сделаешь 255 вложенных using с одним и тем же ObjectId.
2. А что будет в Вашем примере, если открытый объект не удастся привести к Entity? Объект откроется, но не закроется, как я понимаю.
Нужно конечно проверить, но думаю что всё будет нормально, так как ObjectId.Open возвращает DBObject, а он тоже в конечном итоге происходит от DisposableWrapper. Соответственно вызовется его метод Dispose, соторый равносилен Close. Так что всё будет нормально.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Если использовать using, то никогда в такую ситуацию не попадёшь.
Не поверите, уже столкнулся. Происходит обращение к объекту, который был открыт на редактирование то ли самим автокадом, то ли другим методом.
Нужно конечно проверить, но думаю что всё будет нормально
Как я понимаю, Ваш код эквивалентен этому:
Код - C# [Выбрать]
  1. Entity ent = id.Open(...) as Entity;
  2. using (ent)
  3. {
  4.  
  5. }
  6.  
А ent = null, если id передан не для Entity. И ссылка на открытый объект в using не передастся.

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Не поверите, уже столкнулся. Происходит обращение к объекту, который был открыт на редактирование то ли самим автокадом, то ли другим методом.
Теоретически возможно. Но тут речь не про 255 "на чтение" с дополнительной попыткой открыть "на чтение", а открытый "на запись" и вторая попытка открытия. А это согласись разные вещи.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Как я понимаю, Ваш код эквивалентен этому:
А я думаю, что не эквивалентен. Тут или очень хорошо знать теорию, или экспериментировать.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Но тут речь не про 255 "на чтение" с дополнительной попыткой открыть "на чтение", а открытый "на запись" и вторая попытка открытия. А это согласись разные вещи.
Да я согласен, конечно :) Я же не с целью поспорить, а просто хочу предупредить о возможной опасности.
Тут или очень хорошо знать теорию, или экспериментировать.
Проверил вот таким кодом, в 19 строке исключение, что объект открыт на запись:
Код - C# [Выбрать]
  1. public class DBObjectsTest
  2. {
  3.     /// <summary>
  4.     /// Работа с объектами БД чертежа
  5.     /// </summary>
  6.     [CommandMethod("DBObjectsTest")]
  7.     public void DBObjectsTestRun()
  8.     {
  9.         Document adoc = Application.DocumentManager.MdiActiveDocument;
  10.         Database db = adoc.Database;
  11.  
  12.         ObjectId id = db.CurrentSpaceId;
  13.  
  14.         using (Entity ent = id.Open(OpenMode.ForWrite) as Entity)
  15.         {
  16.  
  17.         }
  18.  
  19.         using (DBObject obj = id.Open(OpenMode.ForRead))
  20.         {
  21.  
  22.         }
  23.     }
  24. }
  25.  

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Значит я не прав и приведение следует делать внутри using если не уверен в типе объекта.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение