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

ADN Club => AutoCAD .NET API => Тема начата: dmitrymaslakov от 11-01-2019, 06:11:48

Название: Передача экземпляра класса Transaction как параметр метода
Отправлено: dmitrymaslakov от 11-01-2019, 06:11:48
Если я передам объект транзакции в параметр метода не нарвусь ли я на какие нибудь "подводные камни" при сбое в методе NewMethod()? Или лучше для каждого метода создавать свой экземпляр транзакции?

Пример кода:

   
Код - C# [Выбрать]
  1. class MyClass
  2.     {
  3.         void MyMethod()
  4.         {
  5.             using (Db.Transaction tr = db.TransactionManager.StartTransaction())
  6.             {
  7.                 NewMethod(tr);
  8.                 //...//
  9.                 tr.Commit();
  10.             }
  11.         }
  12.  
  13.         void NewMethod(Db.Transaction tr)
  14.         {
  15.             //...//
  16.             Ed.PromptEntityResult res = ed.GetEntity(options);
  17.             Db.BlockReference Shield_Ref = tr.GetObject(res.ObjectId, Db.OpenMode.ForRead, false, true) as Db.BlockReference;
  18.             //...//
  19.  
  20.         }
  21.     }
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: trir от 11-01-2019, 07:20:58
GetEntity при открытой транзакции? хм...
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: Вильдар от 11-01-2019, 08:33:58
Да можно передавать транзакцию.
Обычно стартую одну транзакцию на команду.
Можно открыть объект без транзакции:
Код - C# [Выбрать]
  1. ObjectId.GetObject()
При этом должна быть запущена транзакция.
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: avc от 11-01-2019, 09:02:08
Можно открыть объект без транзакции
... и нарваться на сбои,  потому что никто не гарантирует, что объект открыт в МОЕЙ транзакции, а не каком-то другом потоке. Я не мало времени потратил убирая у себя в коде явные и неявные обращения к toptransaction. Да,  теперь почти все мелкие статические методы имеют лишний аргумент - транзакцию. А сложные открывают свою. Это захламляет код,  но повышает стабильность работы.
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: Александр Пекшев aka Modis от 11-01-2019, 09:43:53
ИМХО. Транзакцию нужно открывать только тогда, когда вы собираетесь менять объект. И лучше всего область видимости (scope) вашей транзакции сделать по возможности минимальной. Открыть один раз где-то в начале - так себе идея.
А вообще все зависит от конкретной задачи и архитектуры кода
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: dmitrymaslakov от 11-01-2019, 10:58:27
Александр Пекшев aka Modis, согласен с тобой. Я привёл абстрактный пример. Меня смущал вопрос, что когда программа передаёт управление NewMethod(), то в нём отсутствует Commit(). Как бы вела себя программа при неполадки в NewMethod()? Будут ли закрыты открытые объекты? Или может есть другие сюрпризы, о которых я не знаю. Но avc и Вильдар, мои сомнения сняли и передавать транзакцию в метод все таки можно.
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: trir от 11-01-2019, 11:00:14
если в NewMethod будет ошибка - посыпится вся транзакция
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: avc от 11-01-2019, 11:01:29
Но avc и Вильдар, мои сомнения сняли и передавать транзакцию в метод все таки можно.
Можно, можно. На то она и транзакция, чтоб откатить или применить сразу все изменения, не зависимо от того в каком методе они сделаны. Commit само собой должен быть в том методе, где создана транзакция, прямо перед ее уничтожением.
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: Вильдар от 11-01-2019, 11:12:13
... и нарваться на сбои,  потому что никто не гарантирует, что объект открыт в МОЕЙ транзакции, а не каком-то другом потоке.
Что-то страшное.
Выполняется команда, в основном потоке.
Если не мудрить с потоками, то такая ситуация исключена.
Транзакцию нужно открывать только тогда, когда вы собираетесь менять объект.
А для чтения не нужно открывать транзакцию?
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: avc от 11-01-2019, 11:28:44
Что-то страшное
Это МОЯ команда в одном потоке, а в это время Автокад по таймерам и событиям живет своей жизнью, открывает новые транзакции. И никто мне не обещает, что эту чужую транзакцию я не словлю в toptransaction. И попробуйте тогда угадать, кто первый уничтожит нужный мне объект, я или чужая транзакция... По факту отказ от обращений к toptransaction привел к исчезновению случайных сбоев во время закрытия транзакций, которые меня сильно доставали. Правда это были не фатальные ошибки, но совершенно непредсказуемые. Вот типичное сообщение о такой ошибке:

Error:
  Type: System.InvalidOperationException
  Message: Operation is not valid due to the current state of the object.
  Source: Acdbmgd
  TargetSite: Void CheckTopTransaction()

System.InvalidOperationException: Operation is not valid due to the current state of the object.
   at Autodesk.AutoCAD.DatabaseServices.Transaction.CheckTopTransaction()
   at Autodesk.AutoCAD.DatabaseServices.Transaction.DeleteUnmanagedObject()
   at Autodesk.AutoCAD.Runtime.DisposableWrapper.!DisposableWrapper()
   at Autodesk.AutoCAD.Runtime.DisposableWrapper.Dispose(Boolean A_0)
   at Autodesk.AutoCAD.Runtime.DisposableWrapper.Dispose()
   at ....

Если кто расскажет как использовать toptransaction и не выстрелить себе в ногу не нарваться на такие ошибки - буду премного благодарен.
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: Вильдар от 11-01-2019, 11:36:10
Это МОЯ команда в одном потоке, а в это время Автокад по таймерам и событиям живет своей жизнью, открывает новые транзакции.
Команда выполняется в главном потоке приложения (единственном), при этом не допускаются обращения к апи автокада из других потоков.
Соответственно, по таймерам, в других потоках, нельзя запустить транзакцию и т.п.
Возможно нехорошие плагины этим занимаются.

С подобными ошибками, вроде, не сталкивался пока.
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: Владимир Шу от 11-01-2019, 11:44:53
Стырено тут: https://spiderinnet1.typepad.com/blog/2014/05/net-crash-autocad-14-new-dbobjectentity-but-not-dispose-of-it.html?cid=6a0153928ee38e970b01a73dc6316f970d#comment-6a0153928ee38e970b01a73dc6316f970d (https://spiderinnet1.typepad.com/blog/2014/05/net-crash-autocad-14-new-dbobjectentity-but-not-dispose-of-it.html?cid=6a0153928ee38e970b01a73dc6316f970d#comment-6a0153928ee38e970b01a73dc6316f970d)
Цитировать
>>How would you handle the case of something wrong after the New DBObject but before the Transaction.AddNewlyCreatedDBObject() then?<<
I prefer do not use transaction at all (as Fenton Webb: http://adndevblog.typepad.com/autocad/2012/06/creating-a-polyline3d-without-using-transactions.html )
Posted by: Alexander Rivilis | 05/18/2014 at 07:14 AM
Можно открыть объект без транзакции:
   
Код - C# [Выбрать]
  1. ObjectId.GetObject()
Я думаю без транзакции более правильно так:
ObjectId.Open(OpenMode.ForRead)
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: Вильдар от 11-01-2019, 12:14:34
Я думаю без транзакции более правильно так:
ObjectId.Open(OpenMode.ForRead)
Имелось ввиду без передачи объекта транзакции явно. Но с запущенной транзакцией.
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: Дмитрий Загорулькин от 11-01-2019, 13:20:26
Если я передам объект транзакции в параметр метода не нарвусь ли я на какие нибудь "подводные камни" при сбое в методе NewMethod()? Или лучше для каждого метода создавать свой экземпляр транзакции?
Именно из-за передачи транзакции в метод как параметра проблем не будет. А вот из-за использования транзакции в некоторых случаях могут возникнуть проблемы. Но есть шанс, что вы на них никогда не наткнётесь.
Транзакцию нужно открывать только тогда, когда вы собираетесь менять объект.
Не совсем согласен с жёсткостью формулировки. Почему именно нужно? Я, например, очень часто и объекты изменяю без транзакции. И, в то же время, для чтения свойств в какой-нибудь простой команде использую транзакции.
А вообще все зависит от конкретной задачи и архитектуры кода
Вот тут абсолютно согласен!
Название: Re: Передача экземпляра класса Transaction как параметр метода
Отправлено: Привалов Дмитрий от 11-01-2019, 13:20:40
Если я передам объект транзакции в параметр метода не нарвусь ли я на какие нибудь "подводные камни" при сбое в методе NewMethod()? Или лучше для каждого метода создавать свой экземпляр транзакции?
По идее блок USING гарантирует вызов метода Dispose в любом случае, даже в случае ошибки в методе NewMethod(). Однако он не спасет от ошибок работы с Database и DbObjects, которые частенько "убивают" AutoCAD, делая дальнейшие вызовы Commit и Dispose у транзакции бессмысленными. (по крайней мере я не доверяю работе AutoCAD после fatal error и не сохраняю результат работы.)

Т.е. разделяя работу с транзакцией на методы, на мой взгляд ты ничего не потеряешь в стабильности.

А вот создавать или нет для каждого метода свой экземпляр транзакции, зависит от логики приложения. Лишние создание/закрытие транзакций могут ощутимо просадить производительность твоего приложения.