Transaction или GetObject?

16.09.2012

    Редактировать объекты базы данных можно как с использованием транзакции, так и без неё, однако использование транзакции является всё же более предпочтительным способом, рекомендуемым компанией Autodesk. 

    Согласно заявлению разработчиков Autodesk, чтение и редактирование отдельных объектов без использования транзакции происходит быстрее, чем с ней. На практике эта разница в скорости не будет ощущаться конечным пользователем, причём в случае обработки большого набора данных, использование транзакции позволит выполнить эту обработку даже быстрее. Компания Autodesk рекомендует использовать "классический" способ редактирования объектов - открывая их посредством транзакции.

Внимание! 

Существующие методы DBObject.Close() и DBObject.Cancel(), помечены атрибутом [Obsolete("Use Transaction instead")] в документации ObjectARX 2013. Метод ObjectId.Open(), согласно указанной документации, не содержит атрибута Obsolete, однако по факту, если в программе ILSpy посмотреть для AutoCAD 2013 SP1.1 содержимое настоящей библиотеки AcDbMgd.dll, а не её заглушку из ObjectARX 2013, то мы увидим, что этот атрибут всё же имеется: [Obsolete("For advanced use only. Use GetObject instead")]. Т.о. для получения объекта, вместо метода  ObjectId.Open() следует использовать метод ObjectId.GetObject(). Использование этого метода будет безопасным, например в случае, когда вам нужно быстро открыть объект только для чтения.

    Для того, чтобы редактировать объекты без использования транзакции, можно воспользоваться методами ObjectId.GetObject() и DBObject.Close(): первый открывает объект для редактирования, а второй - сохраняет выполненные изменения. Если изменения ещё не зафиксированы и требуется их отменить, то делается это с помощью метода DBObject.Cancel().

    Если вы завершаете работу с объектом посредством вызова метода DBObject.Close(), сохраняя тем самым выполненные изменения, то вызывать после этого метод DBObject.Dispose() нет необходимости, поскольку этот метод будет вызван в процессе работы DBObject.Close().

    Если вы завершаете работу с объектом посредством вызова метода DBObject.Dispose(), то вызывать после этого метод DBObject.Close() не следует, поскольку этот метод вызывается автоматически в процессе работы DBObject.Dispose() и повторный вызов DBObject.Close() приведёт к возникновению Fatal Error.

Редактирование объектов непосредственно, без использования транзакции, в некоторых случаях может быть опасным. Например, если вы держите открытыми одновременно несколько объектов, то вам нужно будет либо для каждого из них создать свою конструкцию using(), либо try/catch, иначе, если у вас что-то пойдёт не так и произойдёт исключение (Exception) - другие ранее открытые вами вне транзакции объекты могут остаться не закрытыми.

    Второй пример опасного использования ObjectId.GetObject()/DBObject.Close() - это когда данный способ применяется внутри блока using() и открытые объекты были изменены. Когда вы используете конструкцию using() для автоматического уничтожения объектов, созданных с помощью ObjectId.GetObject() - во время их уничтожения (код экземплярного метода Dispose()) происходит вызов экземплярного метода Close(), ФИКСИРУЯ тем самым любые выполненные над объектом изменения. Это равносильно вызову метода Transaction.Commit() для объекта, открытого с помощью Transaction.GetObject(). Использование конструкции using() приводит к вызову метода Dispose() объекта независимо от того, завершилось ли выполнение кода успешно, или же завершено преждевременно - в результате возникновения исключения (Exception).

    Если же объекты для редактирования вы открыли с помощью транзакции и по каким-либо причинам произойдёт исключение раньше, чем вы вызовете метод Transaction.Commit(), то автоматически будет вызван Transaction.Abort(), в результате чего все незавершённые изменения, выполненные в рамках данной транзакции, будут отменены. Завершённые изменения - это изменения, зафиксированные вызовом метода Transaction.Commit().

    Т.о. механизм ObjectId.GetObject()/DBObject.Close() может быть безопасно использован в том случае, когда вам необходим доступ лишь для чтения, но если вам нужно редактировать объекты, то следует быть очень осторожным, помня о тех подводных камнях, которые описаны выше.

    Объект OpenCloseTransaction, возвращаемый методом TransactionManager.StartOpenCloseTransaction(), разработан для предоставления механизму ObjectId.GetObject()/DBObject.Close() поведения, подобному транзакции. Использование этого объекта гарантирует, что если транзакция будет прервана, то все объекты в рамках этой транзакции будут закрыты с помощью метода DBObject.Cancel(), что является более предпочтительным, чем закрытие с использованием метода DBObject.Close(). Объект OpenCloseTransaction служит двум основным целям: удостовериться, что все открытые в рамках транзакции объекты будут закрыты, когда транзакция будет применена либо прервана, а так же убедиться в том, что в случае прерывания транзакции для всех открытых объектов будет вызван экземплярный метод DBObject.Cancel().

    Согласно сведениям Autodesk, существуют некоторые издержки, связанные с прерыванием транзакции, т.е. издержки, возникающие при вызове метода Transaction.Abort(). Если в своём коде вы в явном виде не вызываете метод Transaction.Commit(), то транзакция прерывается в методе Transaction.Dispose(). Соответственно, даже если вы открывали транзакцию только для чтения, то использование Transaction.Commit() позволит вашему коду работать быстрее.

Источники информации:

Comments