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

ADN Club => AutoCAD .NET API => Тема начата: Дмитрий Загорулькин от 29-08-2014, 15:20:33

Название: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 29-08-2014, 15:20:33
Здравствуйте.
Интересует как правильно выполнять открытие объектов для чтения свойств без использования транзакций. Так как такой подход является в последнее время нерекомендуемым, информации по правильному использованию этой возможности не найти нигде.
В интернете есть примеры с использованием методов ObjectId.Open/Close и с использованием OpenCloseTransaction.
Вопросы по первому способу:
Встречал такие варианты:
1.
Код - C# [Выбрать]
  1. try
  2. {
  3.     DBObject dbObj = objId.Open(OpenMode.ForRead);
  4.     // <как-то используем объект>
  5.     DBObject.Close();
  6.     DBObject.Dispose();
  7. }
  8. catch
  9. {
  10.     // <Обработка исключения>
  11. }
2.
Код - C# [Выбрать]
  1. using (DBObject dbObj = objId.Open(OpenMode.ForRead))
  2. {
  3.     // <как-то используем объект>
  4. }
Как наиболее правильно и безопасно применять?

Метод Open помечен как "нерекомендуемый", чем это может грозить?

По второму способу:
Нужно ли выполнять Commit для такой транзакции, если объекты не изменяются? Влияет ли это как-то на быстродействие? Какие есть ограничения применения? А ограничения какие-то должны быть, потому что, когда я просто изменил в своем коде Transaction на OpenCloseTransaction, а StartTransaction на StartOpenCloseTransaction, у меня программа стала вызывать фатальную ошибку в AutoCAD (Civil 3D - может это важно). Вернул все обратно - ошибка пропала. Причем в режиме отладки эта ошибка не отлавливается :(

В общем, буду благодарен за любую информацию по теме! Может быть, есть какие-то полезные статьи или материалы по этому вопросу?
Заранее спасибо!
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 29-08-2014, 15:27:03
Как наиболее правильно и безопасно применять?
Второй вариант. Первый вариант совсем небезопасный, так еще и неграмотно записан и .Dispose() не нужен. Это тоже самое что дважды вызвать .Close()
Нужно ли выполнять Commit для такой транзакции, если объекты не изменяются?
Во втором варианте у тебя нет никакой транзакции, так что .Commit() не нужен. Да и кому ты будешь его делать?
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 29-08-2014, 15:28:52
А ограничения какие-то должны быть, потому что, когда я просто изменил в своем коде Transaction на OpenCloseTransaction, а StartTransaction на StartOpenCloseTransaction, у меня программа стала вызывать фатальную ошибку в AutoCAD (Civil 3D - может это важно). Вернул все обратно - ошибка пропала. Причем в режиме отладки эта ошибка не отлавливается :(
Скорее всего у тебя что-то в коде некорректно.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 29-08-2014, 15:33:15
То есть, я правильно понимаю, что для варианта ObjectId.Open/Close, DBObject.Close() то же самое, что и DBObject.Dispose()? Или DBObject.Dispose() включает в себя DBObject.Close()?
Цитировать
Да и кому ты будешь его делать?
Не знаю... Метод есть же :)
Commit вообще не надо использовать, или не надо использовать только когда не меняю объектов?
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 29-08-2014, 15:34:49
Из примеров:
http://adn-cis.org/sozdanie-polyline3d-bez-ispolzovaniya-tranzakczii.html
http://adn-cis.org/preobrazovanie-polyline-v-polyline2d.html
http://adndevblog.typepad.com/autocad/2012/08/the-right-tools-for-the-job-autocad-part-5.html
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 29-08-2014, 15:35:33
Не знаю... Метод есть же :)
Метод есть у транзакции, а транзакции у тебя нет!
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 29-08-2014, 15:40:21
Commit вообще не надо использовать, или не надо использовать только когда не меняю объектов?
Commit - это метод транзакции. У Entity/DBObject таких методов нет.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: German от 29-08-2014, 15:45:09
Опять запутали с Бушманом на пару ???
Метод есть у транзакции, а транзакции у тебя нет!
Андрею надо поправить пример 3.1 http://bushman-andrey.blogspot.ru/2013/01/database-autocad.html?
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 29-08-2014, 15:47:27
Цитировать
Скорее всего у тебя что-то в коде некорректно.
Там было внутри использование еще одной транзакции во вспомогательном методе. Я ее тоже заменил на OpenCloseTransaction, ошибка больше не выскакивает. Получается, что совместное использование OpenCloseTransaction и Transaction может приводить к конфликтам?
Цитировать
Commit - это метод транзакции. У Entity/DBObject таких методов нет.
Я имел в виду, нужно ли вызывать этот метод для OpenCloseTransaction?
Цитировать
Из примеров:
Спасибо! Буду изучать/пробовать.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 29-08-2014, 15:50:52
Я имел в виду, нужно ли вызывать этот метод для OpenCloseTransaction?
В этом случае - да. Но код второго примера у тебя без транзакции - на это я и отвечал.
Получается, что совместное использование OpenCloseTransaction и Transaction может приводить к конфликтам?
Да. Так лучше не делать.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 29-08-2014, 15:53:37
Андрею надо поправить пример 3.1 http://bushman-andrey.blogspot.ru/2013/01/database-autocad.html?
Там всё в порядке - у него есть транзакция.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 29-08-2014, 15:58:47
Андрею надо поправить пример 3.1 http://bushman-andrey.blogspot.ru/2013/01/database-autocad.html?
Какая хорошая статья! Почему-то я ее поиском не нашел, хотя помнил, что у Андрея что-то такое было... Спасибо!
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 29-08-2014, 16:15:49
Еще, похоже, что метод ObjectId.GetObject не работает, если запущена не транзакция, а ее эмуляция с помощью OpenCloseTransaction.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 29-08-2014, 17:48:06
Еще, похоже, что метод ObjectId.GetObject не работает, если запущена не транзакция, а ее эмуляция с помощью OpenCloseTransaction.
Да. ObjectId.GetObject требует чтобы была запущена реальная транзакция.
Кстати, по поводу вложенных OpenCloseTransaction. Если в таких транзакциях объекты только для чтения, то в принципе вложенные транзакции возможны. Есть четкие ограничения:
1) открытый для чтения объект может быть повторно открыт еще для чтения (но не больше чем 255 раз);
2) открытый для чтения объект не может быть повторно открыт для записи;
2) открытый для записи объект не может быть повторно открыт никак.
Немного упрощенно, но так.
Для обычной транзакции этих условий нет.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 01-09-2014, 11:13:16
Спасибо!
Интересно, Вы это опытным путем установили (255 открывали на чтение? :) ) или где-то есть материалы по этому вопросу?
И можно ли как-то проверить, что объект уже открыт на чтение или запись?
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 01-09-2014, 12:16:56
Интересно, Вы это опытным путем установили (255 открывали на чтение? :) ) или где-то есть материалы по этому вопросу?
Autodesk ObjectARX for AutoCAD 20XX: Developer Guide->ObjectARX Introductory Concepts->Database Objects->Opening and Closing Database Objects
Там это всё расписано более подробно, чем я написал.
И можно ли как-то проверить, что объект уже открыт на чтение или запись?
Да:
DBObject.IsReadEnabled
DBObject.IsWriteEnabled
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 01-09-2014, 13:47:17
Здорово! Спасибо!
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 12-09-2014, 13:40:06
А нет ли случайно каких-то ограничений по использованию методов UpgradeOpen и SwapIdWith при открытии объектов без транзакции?
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 12-09-2014, 13:48:26
А нет ли случайно каких-то ограничений по использованию методов UpgradeOpen и SwapIdWith при открытии объектов без транзакции?
У тебя возникла проблема? Тогда уточни какая. Возможно она комплексная.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 12-09-2014, 13:51:45
Вообще-то UpgradeOpen нельзя вызвать в событии изменения примитива, когда DBObject.IsNotifyEnabled == true
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 12-09-2014, 14:02:01
Да не так просто уточнить, сам пока пытаюсь разобраться...
Была одна транзакция у меня по замене одного блока другим. Все работало, но медленно. Решил заменить на OpenCloseTransaction и посыпалось...
Вроде бы в одном разобрался.
У меня выдавал ошибку метод SwapIdWith. Сперва ругался на то, что объект открыт для чтения. Я поставил перед вызовом метода UpgradeOpen - стало ругаться, что объект открыт для записи.
Получается, что если объект открыт на чтение или запись без использования транзакции, то метод SwapIdWith выдает ошибку (тот объект, id которого используется как аргумент otherId). Сейчас проверяю эту мысль.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 12-09-2014, 14:05:14
Получается, что если объект открыт на чтение или запись без использования транзакции, то метод SwapIdWith выдает ошибку (тот объект, id которого используется как аргумент otherId). Сейчас проверяю эту мысль.
Ну вообще-то этот объект должен быть закрыт. Обычное правило - если передается ObjectId, то объект должен быть закрыт.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 12-09-2014, 14:11:59
Да, теперь все в порядке!
А с обычной транзакцией "прокатывало" подсунуть ObjectId открытого объекта :)
Обычное правило - если передается ObjectId, то объект должен быть закрыт.
Спасибо, учту на будущее!
Самое замечательное, что после исправления этой ошибки, даже с обычной транзакцией замена происходит очень быстро!
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 12-09-2014, 14:36:36
А с обычной транзакцией "прокатывало" подсунуть ObjectId открытого объекта :)
Обычная транзакция разбаловала программистов. Но иногда это выходит боком так, что найти причину бага становится очень непростой проблемой.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Андрей Бушман от 15-09-2014, 11:55:15
Я имел в виду, нужно ли вызывать этот метод для OpenCloseTransaction?
В этом случае - да.
Не обязательно, т.к.:
Цитата: Андрей Бушман
Обратите внимание, что у Transaction и у OpenCloseTransaction похожее использование, но разное действие по умолчанию:
у Transaction, по умолчанию вызывается метод Abort(), тем самым отменяя все изменения, а у OpenCloseTransaction по умолчанию вызывается метод Close(), тем самым наоборот - сохраняя все выполненные изменения.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 15-09-2014, 20:05:24
Надо же какое совпадение! Сегодня часа 2 бился над проблемой - почему Civil 3D валится на определенной операции. В итоге, добавление метода Commit в транзакцию (а точнее - обеспечение его 100% вызова в любой ситуации) решило проблему.
Видеодемонстрация (видео длинное получилось, т.к. загружается Civil небыстро):
https://screencast.autodesk.com/main/details/fec694d8-ab7e-4793-a262-d8feb3ae6680
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Андрей Бушман от 15-09-2014, 21:09:31
Надо же какое совпадение! Сегодня часа 2 бился над проблемой - почему Civil 3D валится на определенной операции. В итоге, добавление метода Commit в транзакцию (а точнее - обеспечение его 100% вызова в любой ситуации) решило проблему.Видеодемонстрация (видео длинное получилось, т.к. загружается Civil небыстро):https://screencast.autodesk.com/main/details/fec694d8-ab7e-4793-a262-d8feb3ae6680

Видео мелкое, очень плохо видно. Я не нашёл как можно на весь экран его растянуть. Пишу о том, что сразу же бросилось в глаза:

1. Вложенный блок using (в которой инициализируеся pView) здесь лишний. Он был бы не лишним в том случае, если бы ты открывал объект без использования транзакции и её эмуляции. При эмуляции код следует писать так, как при использовании транзакции, не забывая лишь о том, о чём я писал в предыдущем своём сообщении - пусть Александр Наумович поправит меня, если я неправ...

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

Попробуй удалить вложенный блок using и закомментируй Commit.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 15-09-2014, 21:52:13
Ха. Хотя я был уверен, что Андрей Бушман прав, но оказалось что это не так.
Достаточно проверить такой простенький код:
Код - C# [Выбрать]
  1. // TestOpenClose
  2. [CommandMethod("TestOpenClose")]
  3. public void TestOpenClose()
  4. {
  5.   Document doc = AcAp.Application.DocumentManager.MdiActiveDocument;
  6.   Database db = doc.Database;
  7.   AcEd.Editor ed = doc.Editor;
  8.   using (OpenCloseTransaction tr =
  9.            doc.TransactionManager.StartOpenCloseTransaction()) {
  10.     Circle cr = new Circle();
  11.     cr.SetDatabaseDefaults(db);
  12.     cr.Radius = 10;
  13.     BlockTableRecord mspace =
  14.              tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
  15.     mspace.AppendEntity(cr);
  16.     tr.AddNewlyCreatedDBObject(cr, true);
  17.     tr.Commit(); // Если это строка закомментарена, то Круг не появляется
  18.   }
  19. }
Т.е. поведение OpenCloseTransaction сделали подобным поведению обычной Transaction.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 15-09-2014, 21:53:18
1. Вложенный блок using (в которой инициализируеся pView) здесь лишний.
А вот это точно.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 15-09-2014, 22:10:57
Да я понимаю, что лишний. Это так сказать плод творческих поисков :)
Думал: мало ли, вдруг объект остается открытым и потому рушится работа.
Уберу эту конструкцию, хотя, думаю, что на работе кода это никак не скажется.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Андрей Бушман от 15-09-2014, 22:50:29
Ха. Хотя я был уверен, что Андрей Бушман прав, но оказалось что это не так.
...
Т.е. поведение OpenCloseTransaction сделали подобным поведению обычной Transaction.
Обозначенную информацию я взял не с потолка, а из блогов Автодеск. Вы хотите сказать не "сделали", а "переделали"? Если "да", то в какой версии Автокада это произошло. Сам проверить не могу, т.к. в ближайшие несколько дней доступа к Автокаду у меня нет.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 15-09-2014, 23:23:30
Уберу эту конструкцию, хотя, думаю, что на работе кода это никак не скажется.
Это 100% лишняя конструкция, которая приводит или к повторному .Close объекта или к последовательному вызову .Abort, а затем .Close объекта, что мягко говоря плохо.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Андрей Бушман от 16-09-2014, 11:39:47
Т.е. поведение OpenCloseTransaction сделали подобным поведению обычной Transaction
Сейчас проверил в 2009-м Acade - поведение аналогичное. Ищу в Интернете источник информации, где я прочитал об этом "отличии"...

Пока в обозначенной выше записи блога подправил текст, зачеркнув ошибку и добавив пояснение:
Цитировать
Обратите внимание, что у Transaction и у OpenCloseTransaction похожее использование, но разное действие по умолчанию:
у Transaction, по умолчанию вызывается метод Abort(), тем самым отменяя все изменения, а у OpenCloseTransaction по умолчанию вызывается метод Close(), тем самым наоборот - сохраняя все выполненные изменения.
Зачёркнута ошибочная информация, выяснилось здесь (http://adn-cis.org/forum/index.php?topic=940.msg4166#msg4166).
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Андрей Бушман от 16-09-2014, 13:46:25
Позволил себе немного модифицировать исходный код Александра Наумовича. Это маленький пример того, как можно безболезненно (не внося существенных правок в код) менять способ редактирования объектов (с использованием транзакции или же с её эмуляцией):

Код - C# [Выбрать]
  1. // © Андрей Бушман, 2014
  2. // Commands.cs
  3. using System;
  4. using System.Collections.Generic;
  5.  
  6. #if AUTOCAD
  7. using cad = Autodesk.AutoCAD.ApplicationServices.Application;
  8. using Ap = Autodesk.AutoCAD.ApplicationServices;
  9. using Db = Autodesk.AutoCAD.DatabaseServices;
  10. using Ed = Autodesk.AutoCAD.EditorInput;
  11. using Rt = Autodesk.AutoCAD.Runtime;
  12. #endif
  13.  
  14. [assembly: Rt.CommandClass(typeof(Bushman.CAD.Sandbox.TransactionGames.Commands))]
  15.  
  16. namespace Bushman.CAD.Sandbox.TransactionGames {
  17.     public sealed class Commands {
  18.  
  19.         /// <summary>
  20.         /// За основу взят код Александра Ривилиса:
  21.         /// http://adn-cis.org/forum/index.php?topic=940.msg4166#msg4166
  22.         /// </summary>
  23.         [Rt.CommandMethod("TestOpenClose")]
  24.         public void TestOpenClose() {
  25.             Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  26.             Db.Database db = doc.Database;
  27.             Db.TransactionManager tm = db.TransactionManager;
  28.             Ed.Editor ed = doc.Editor;
  29.  
  30.             Ed.PromptKeywordOptions pso = new Ed.PromptKeywordOptions(
  31.                 "Использовать транзакцию?");
  32.             String yes = "Yes";
  33.             String no = "No";
  34.             pso.Keywords.Add(yes);
  35.             pso.Keywords.Add(no);
  36.             pso.AppendKeywordsToMessage = true;
  37.             pso.Keywords.Default = yes;
  38.  
  39.             Ed.PromptResult pr = ed.GetKeywords(pso);
  40.             if (Ed.PromptStatus.OK != pr.Status) {
  41.                 ed.WriteMessage("Выполнение команды прервано.\n");
  42.             }
  43.  
  44.             // С помощью этой переменной можно управлять тем, будет ли работа
  45.             // выполняться с использованием транзакции или же с её эмуляцией.
  46.             // Т.о. в одном месте изменив значение переменной transaction_use
  47.             // можно одним махом изменить способ редактирования объектов сразу
  48.             // во многих местах исходного кода (в данном примере показано одно
  49.             // такое место):
  50.             Boolean transaction_use = pr.StringResult == yes;
  51.             Func<Db.Transaction> get_tr = null;
  52.  
  53.             if (transaction_use)
  54.                 get_tr = tm.StartTransaction;
  55.             else
  56.                 get_tr = tm.StartOpenCloseTransaction;
  57.  
  58.             // Т.к. OpenCloseTransaction наследуется от Transaction, то можно
  59.             // записать так:
  60.             using (Db.Transaction tr = get_tr()) {
  61.  
  62.                 Db.Circle cr = new Db.Circle();
  63.                 cr.SetDatabaseDefaults(db);
  64.                 cr.Radius = 10;
  65.  
  66.                 Db.BlockTableRecord mspace = tr.GetObject(db.CurrentSpaceId,
  67.                     Db.OpenMode.ForWrite) as Db.BlockTableRecord;
  68.                 mspace.AppendEntity(cr);
  69.                 tr.AddNewlyCreatedDBObject(cr, true);
  70.                 // Если это строка закомментарена, то Круг не появляется:
  71.                 tr.Commit();
  72.             }
  73.         }
  74.     }
  75. }
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Константин Соков от 11-11-2014, 20:56:41
Почитал статьи указанные выше Александром Наумовичем. До этого использовал метод StartTransaction(), после прочтения решил использовать StartOpenCloseTransaction(). Приложение перестало работать. Есть подозрение что не работает из-за того что внутри метода, где используется StartOpenCloseTransaction() вызывается метод где используется StartTransaction(). Может такое быть?
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 11-11-2014, 23:49:05
Может такое быть?
Да. И именно потому что:
внутри метода, где используется StartOpenCloseTransaction() вызывается метод где используется StartTransaction().
Возьми себе за правило:
1) Не смешивать Transaction и OpenCloseTransaction.
2) Не использовать вложенные OpenCloseTransaction.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Константин Соков от 12-11-2014, 00:05:48
Вызов метода из метода считается вложением? Появляется ошибка при использовании StartOpenCloseTransaction(), если использовать StartTransaction(), то все хорошо.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 12-11-2014, 00:28:52
Вызов метода из метода считается вложением?
Нет конечно. Вложенная транзакция, это когда одна транзакция уже работает, а ты в это время создаёшь новую.
Ошибка у тебя из-за того что объект открыт ForRead, а должен быть ForWrite, т.к. ты его модифицируешь. Сообщение совершенно четко тебе об этом указывает.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 12-11-2014, 15:13:45
2) Не использовать вложенные OpenCloseTransaction.
А если я использую вложенные только для открытия на чтение?
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 12-11-2014, 20:58:55
А если я использую вложенные только для открытия на чтение?
Правильный подход! :) Если уровень вложенности не превышает 255 (эту цифру мы уже с тобой обсуждали), то всё нормально.
Вообще-то нормально будет даже если открывать на запись, но (!!!) если один и тот-же объект не открывается больше чем в одной транзакции. Короче говоря для OpenCloseTransaction работают те же правила, что описаны в Autodesk ObjectARX SDK Documents: Developer Guide > ObjectARX Introductory Concepts > ObjectARX Introductory Concepts > Database Objects > Opening and Closing Database Objects
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Константин Соков от 14-11-2014, 13:13:45
Небольшой пример, просто вставка блока, с StartTransaction() - все хорошо, с StartOpenCloseTransaction() - ошибка. Не могу понять почему.
Код - C# [Выбрать]
  1.         static public BlockReference InsertBlock(string name, Point3d basepnt)
  2.         {
  3.             Document acDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
  4.             Database acDb = acDoc.Database;
  5.             Editor ed = acDoc.Editor;
  6.             BlockReference br = null;
  7.  
  8.             //using (Transaction trans = acDb.TransactionManager.StartOpenCloseTransaction())
  9.             using (Transaction trans = acDb.TransactionManager.StartTransaction())
  10.             {
  11.                 BlockTable bt = (BlockTable)acDb.BlockTableId.GetObject(OpenMode.ForWrite);
  12.                 BlockTableRecord btrSpace = trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
  13.  
  14.                 if (!bt.Has(name))
  15.                 {
  16.                     ed.WriteMessage("\nНе найдено опеределение блока \"" + name + "\"");
  17.                     return null;
  18.                 }
  19.  
  20.                 BlockTableRecord btrFind = (BlockTableRecord)bt[name].GetObject(OpenMode.ForWrite);
  21.                 br = new BlockReference(basepnt, btrFind.ObjectId);
  22.  
  23.                 btrSpace.AppendEntity(br);
  24.                 trans.AddNewlyCreatedDBObject(br, true);
  25.  
  26.                 trans.Commit();
  27.             }
  28.             return br;
  29.         }
  30.  
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 14-11-2014, 13:18:10
Замени:
1)
Код - C# [Выбрать]
  1. BlockTable bt = (BlockTable)acDb.BlockTableId.GetObject(OpenMode.ForWrite);
на
Код - C# [Выбрать]
  1. BlockTable bt = (BlockTable)acDb.BlockTableId.GetObject(OpenMode.ForRead);
2)
Код - C# [Выбрать]
  1. BlockTableRecord btrFind = (BlockTableRecord)bt[name].GetObject(OpenMode.ForWrite);
на
Код - C# [Выбрать]
  1. BlockTableRecord btrFind = (BlockTableRecord)bt[name].GetObject(OpenMode.ForRead);
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 14-11-2014, 13:18:57
Строки #11 и #20 в коде и сообщения #12 и #13 в этой теме ;)
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Константин Соков от 14-11-2014, 16:51:50
Замени:
1)
Код - C# [Выбрать]
  1. BlockTable bt = (BlockTable)acDb.BlockTableId.GetObject(OpenMode.ForWrite);
на
Код - C# [Выбрать]
  1. BlockTable bt = (BlockTable)acDb.BlockTableId.GetObject(OpenMode.ForRead);
2)
Код - C# [Выбрать]
  1. BlockTableRecord btrFind = (BlockTableRecord)bt[name].GetObject(OpenMode.ForWrite);
на
Код - C# [Выбрать]
  1. BlockTableRecord btrFind = (BlockTableRecord)bt[name].GetObject(OpenMode.ForRead);
Пробывал, результат один - StartTransaction() - работает, StartOpenCloseTransaction() - не работает
Строки #11 и #20 в коде и сообщения #12 и #13 в этой теме ;)
А это в моей ситуации самое разумное объяснение
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 14-11-2014, 16:54:36
А это в моей ситуации самое разумное объяснение
Да. Я как-то не обратил внимание. Тем не менее исправить нужно и то, и другое.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Константин Соков от 14-11-2014, 17:02:59
Жаль, хотелось рыбку съесть и кости сдать. StartOpenCloseTransaction() в несколько раз шустрее чем StartTransaction(). Думал удастся ускорить приложение только поменяв StartTransaction() на StartOpenCloseTransaction()
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Андрей Бушман от 14-11-2014, 17:49:04
StartOpenCloseTransaction() в несколько раз шустрее чем StartTransaction()
Ой ли? Зависит от конкретной ситуации.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Константин Соков от 14-11-2014, 21:56:19
так-то оно так! Вот бы эту информацию в одном месте увидеть. Андрей, как продвигается идея с написанием книги по AutoCAD .Net API? Лично у меня она стала бы настольной.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Андрей Бушман от 14-11-2014, 22:25:55
Андрей, как продвигается идея с написанием книги по AutoCAD .Net API? Лично у меня она стала бы настольной.
Да я вроде как и не собирался этого делать. Опубликованный мною опрос лишь выявил, что интерес к книгам по данной теме стремится практически к нулю.

UPD
Кстати, Н.Н. Полещук недавно издал книгу (http://www.private.peterlink.ru/poleshchuk/cad/2014/Pr2013-2015.htm) по тематике программирования в AutoCAD. Там есть и глава по AutoCAD .NET API. Если бы позволял объём страниц и время, то можно было бы включить туда и материал по модульному, а так же автономному тестированию .NET плагинов AutoCAD с использованием Gallio и NUnit но в виду обозначенных ограничений материал остался за бортом.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Владимир Шу от 15-11-2014, 07:56:08
Там есть и глава по AutoCAD .NET API. Если бы позволял объём страниц и время, то можно было бы включить туда и материал по модульному, а так же автономному тестированию .NET плагинов AutoCAD с использованием Gallio и NUnit но в виду обозначенных ограничений материал остался за бортом.
А я вот, все надеюсь на статью по этой теме...
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Андрей Бушман от 15-11-2014, 21:08:28
А я вот, все надеюсь на статью по этой теме...
Материал есть, всё никак "руки не дойдут" до его оформления.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 28-11-2014, 15:30:43
Сейчас обнаружил ситуацию, в которой использование OpenCloseTransaction вместо Transaction помогло избавиться от ошибки:
Ситуация - открытие чертежа с внешними ссылками. Режим подгрузки внешних ссылок включен, это значит, что открытие чертежа идет по такому сценарию:
- создание Database открываемого чертежа
- создание Database копии чертежа-ссылки и копирование в нее объектов из чертежа-ссылки
и т.д.

Так вот, если во время этого копирования запросить NOD чертежа-ссылки, в обычной транзакции ловим исключение eNotFromThisDocument, а в ее эмуляции - нет исключения.
Интересно, почему так?
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 29-11-2014, 00:12:35
Интересно, почему так?
Думаешь без подготовленного тестового примера с этим можно разобраться? ;)
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 29-11-2014, 14:54:54
Загрузите этот код и попробуйте открыть чертеж "drawing.dwg".

Код - C# [Выбрать]
  1. using System;
  2.  
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.Runtime;
  5. using Autodesk.AutoCAD.ApplicationServices;
  6.  
  7. namespace ForAcadTests
  8. {    
  9.     public class XRefTest : IExtensionApplication
  10.     {
  11.         public void Initialize()
  12.         {            
  13.             Database.DatabaseConstructed += Database_DatabaseConstructed;
  14.         }
  15.  
  16.         void Database_DatabaseConstructed(object sender, EventArgs e)
  17.         {
  18.             Database db = sender as Database;            
  19.             db.BeginDeepClone += db_BeginDeepClone;
  20.         }
  21.  
  22.         void db_BeginDeepClone(object sender, IdMappingEventArgs e)
  23.         {            
  24.             Database
  25.                 origDb = e.IdMapping.OriginalDatabase;
  26.            
  27.             try
  28.             {
  29.                 using (Transaction tr = origDb.TransactionManager.StartTransaction())
  30.                 //using (Transaction tr = origDb.TransactionManager.StartOpenCloseTransaction())
  31.                 {
  32.                     DBDictionary nod = tr.GetObject(origDb.NamedObjectsDictionaryId, OpenMode.ForRead) as DBDictionary;
  33.                     Application.ShowAlertDialog(string.Format("NOD count: {0}", nod.Count));
  34.                     tr.Commit();
  35.                 }
  36.             }
  37.             catch (Autodesk.AutoCAD.Runtime.Exception ex)
  38.             {
  39.                 Application.ShowAlertDialog(string.Format("Exception: {0}", ex.Message));
  40.             }
  41.         }
  42.  
  43.         public void Terminate()
  44.         {
  45.             //throw new NotImplementedException();
  46.         }
  47.     }
  48. }

Кстати, если try-catch убрать, исключение произойдет тихо и бесшумно... При этом, подписки на события Database перестанут работать.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 29-11-2014, 20:01:28
Замени:
Код - C# [Выбрать]
  1. Database origDb = e.IdMapping.OriginalDatabase;
на
Код - C# [Выбрать]
  1. Database origDb = sender as Database;
Думаю, что всё будет нормально... ;)
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 29-11-2014, 20:18:58
В этом случае, конечно, код-то отработает нормально и без ошибок. :)
Только в sender будет база данных основного чертежа а не ссылки.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 29-11-2014, 22:48:28
Тогда так:
Код - C# [Выбрать]
  1. void db_BeginDeepClone(object sender, IdMappingEventArgs e)
  2. {            
  3.     Database origDb = e.IdMapping.OriginalDatabase;
  4.    
  5.     try
  6.     {
  7.         using (Transaction tr = (sender as Database).TransactionManager.StartTransaction())
  8.         //using (Transaction tr = origDb.TransactionManager.StartOpenCloseTransaction())
  9.         {
  10.             DBDictionary nod = tr.GetObject(origDb.NamedObjectsDictionaryId, OpenMode.ForRead) as DBDictionary;
  11.             Application.ShowAlertDialog(string.Format("NOD count: {0}", nod.Count));
  12.             tr.Commit();
  13.         }
  14.     }
  15.     catch (Autodesk.AutoCAD.Runtime.Exception ex)
  16.     {
  17.         Application.ShowAlertDialog(string.Format("Exception: {0}", ex.Message));
  18.     }
  19. }
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 30-11-2014, 19:13:52
Хм... Да, действительно, так работает... Но это же что получается, из транзакции одной базы получаем объект другой базы? Как такое возможно?  :o
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 30-11-2014, 20:30:16
Но это же что получается, из транзакции одной базы получаем объект другой базы? Как такое возможно?
А как возможно, что в событии одной базы (sender as Database) в качестве исходной фигурирует совсем другая база (e.IdMapping.OriginalDatabase) ? ;)
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Дмитрий Загорулькин от 30-11-2014, 20:34:38
Ну тут все логично, на мой взгляд. Просто событие генерирует та база, в которую копируют. Ссылка на нее есть в e.IdMapping.DestinationDatabase. Вообще, хороший вопрос, какая из двух баз (исходная или целевая) генерирует это событие?
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Александр Ривилис от 30-11-2014, 20:45:11
Вообще, хороший вопрос, какая из двух баз (исходная или целевая) генерирует это событие?
Генерирует та, которая sender
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Андрей Бушман от 31-03-2015, 19:49:25
А я вот, все надеюсь на статью по этой теме...
Пример исходного кода модульных тестов, компилирующихся для платформ Gallio и NUnit выложен здесь (http://bushman-andrey.blogspot.ru/2015/03/testcase-nunit-gallio.html). Там же дана и ссылка на архив с HTML-файлами отчётов, автоматически сгенерированных в процессе пакетного запуска тестов в AutoCAD 2009-2015.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Владимир Шу от 01-04-2015, 08:10:36
Андрей Бушман, спасибо. Прочитав первый раз мало что понял. Видимо еще не дорос. Буду изучать.
Название: Re: Открытие объектов без использования транзакции.
Отправлено: Андрей Бушман от 01-04-2015, 10:38:10
Прочитав первый раз мало что понял. Видимо еще не дорос. Буду изучать.
Я не запрещаю задавать конструктивные вопросы по теме. :) Хорошую книгу о том, как грамотно создавать автономные и интеграционные тесты, в т.ч. и под такие закрытые системы как AutoCAD, я указывал здесь (http://adn-cis.org/forum/index.php?topic=400.0), в п.21.

Кроме того, Gallio имеет неплохую offline документацию, доступную в меню Пуск -> Все программы -> Gallio -> Offline Documentation, а документация по NUnit присутствует online (http://www.nunit.org/index.php?p=documentation).

Gallio может выдавать отчёты о результатах теста как в формате XML, так и в формате HTML. В то же время NUnit может генерировать только XML. Для получения HTML на основе XML отчётов NUnit я рекомендую пользоваться утилитой NUnitOrange (http://relevantcodes.com/nunit-orange-nunit-html-report-generator/).