Проблема. Запись в ExtensibleStorage при обработке события DocumentSavingAs

Автор Тема: Проблема. Запись в ExtensibleStorage при обработке события DocumentSavingAs  (Прочитано 3478 раз)

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

Оффлайн Кирилл ЗахаровАвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 119
  • Карма: 5
Здравствуйте!
Я столкнулся с очень странной проблемой в работе с API Revit 2018.

Короткое описание проблемы такое:
Если обработчик события Application.DocumentSavingAs выполняет запись данных в ExtensibleStorage сохраняемого документа (строчка кода такого вида - doc.ProjectInformation.SetEntity(entity)),
то после этого все объекты, которые будут созданы в этом документе до его закрытия не сохранятся.

Более подробное описание в коде:
Код - C# [Выбрать]
  1. #region Namespaces
  2. using System;
  3. using System.Collections.Generic;
  4. using Autodesk.Revit.ApplicationServices;
  5. using Autodesk.Revit.Attributes;
  6. using Autodesk.Revit.DB;
  7. using Autodesk.Revit.DB.Events;
  8. using Autodesk.Revit.DB.ExtensibleStorage;
  9. using Autodesk.Revit.UI;
  10. #endregion
  11.  
  12. namespace RevitTestAddin
  13. {
  14.     /// <summary>
  15.     /// 2 тестовых сценария:
  16.     ///
  17.     /// 1. Открыть существующий проект
  18.     /// -> Создать 2-3 любых объекта (я создавал балки)
  19.     /// -> Сохранить проект
  20.     /// -> Закрыть проект
  21.     /// -> Открыть проект
  22.     /// -> Все работает нормально
  23.     /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  24.     ///
  25.     /// 2. Создать новый проект или открыть существующий
  26.     /// -> Сохранить проект как с изменением расположения
  27.     /// -> Создать 2-3 любых объекта
  28.     /// -> Сохранить проект
  29.     /// -> Закрыть проект
  30.     /// -> Открыть проект
  31.     /// -> Созданные объекты исчезли!!!
  32.     /// </summary>
  33.     class App : IExternalApplication
  34.     {
  35.         public static Guid Guid { get; private set; }
  36.         public static Schema Schema { get; private set; }
  37.  
  38.         /// <summary>
  39.         /// Создание схемы для сохранения данных о объектах чертежа
  40.         /// </summary>
  41.         static App()
  42.         {
  43.             Guid = new Guid("d05893e6-c01e-44e9-8fbb-0d140a26015b");
  44.             SchemaBuilder schemaBuilder = new SchemaBuilder(Guid);
  45.             schemaBuilder.SetReadAccessLevel(AccessLevel.Public);
  46.             schemaBuilder.SetWriteAccessLevel(AccessLevel.Public);
  47.  
  48.             schemaBuilder.AddArrayField("ObjectArr", typeof(ElementId));
  49.  
  50.             schemaBuilder.SetSchemaName("ObjectsSchema");
  51.  
  52.             Schema = schemaBuilder.Finish();
  53.         }
  54.  
  55.  
  56.  
  57.         public Result OnStartup(UIControlledApplication a)
  58.         {
  59.             a.ControlledApplication.ApplicationInitialized += OnApplicationInitialized;
  60.             return Result.Succeeded;
  61.         }
  62.  
  63.  
  64.         void OnApplicationInitialized(object sender, ApplicationInitializedEventArgs e)
  65.         {
  66.             Application app = sender as Application;
  67.  
  68.             //Подпись на события сохранения чертежа и сохранения с новым именем
  69.             //Оба обработчика событий запускают метод, который создает Entity по схеме, описанной выше
  70.             app.DocumentSaving += DocumentSaving_EventHandler;
  71.             app.DocumentSavingAs += DocumentSavingAs_EventHandler;
  72.         }
  73.  
  74.  
  75.         public static void DocumentSaving_EventHandler(object sender, DocumentSavingEventArgs e)
  76.         {
  77.             Document doc = e.Document;
  78.             SaveDocumentObjectIds(doc);
  79.         }
  80.  
  81.         public static void DocumentSavingAs_EventHandler(object sender, DocumentSavingAsEventArgs e)
  82.         {
  83.             Document doc = e.Document;
  84.             SaveDocumentObjectIds(doc);
  85.         }
  86.  
  87.         /// <summary>
  88.         /// Создание Entity
  89.         /// Запись всех id объектов документа в Entity
  90.         /// Запись Entity в ProjectInfo
  91.         /// </summary>
  92.         /// <param name="doc"></param>
  93.         private static void SaveDocumentObjectIds(Document doc)
  94.         {
  95.             if (!doc.IsFamilyDocument)
  96.             {
  97.                 Entity entity = new Entity(Schema);
  98.                 IList<ElementId> objectArr = new List<ElementId>();
  99.                 FilteredElementCollector familyInstanceCollector = new FilteredElementCollector(doc);
  100.                 familyInstanceCollector.OfClass(typeof(FamilyInstance));
  101.                 foreach (Element elem in familyInstanceCollector)
  102.                 {
  103.                     objectArr.Add(elem.Id);
  104.                 }
  105.                 entity.Set("ObjectArr", objectArr);
  106.  
  107.                 if (entity.IsValid())
  108.                 {
  109.                     using (Transaction trans = new Transaction(doc))
  110.                     {
  111.                         trans.Start("SaveDataIntoDocument");
  112.                         doc.ProjectInformation.SetEntity(entity);
  113.                         trans.Commit();
  114.                     }
  115.                 }
  116.             }
  117.  
  118.         }
  119.  
  120.  
  121.         public Result OnShutdown(UIControlledApplication a)
  122.         {
  123.             return Result.Succeeded;
  124.         }
  125.     }
  126. }
  127.  
Тестовый проект прилагаю.

Это проблема самого API или я что-то не так делаю?
Как решить эту проблему?
« Последнее редактирование: 16-02-2018, 11:09:46 от Кирилл Захаров »

Оффлайн Алексей Кузин

  • ADN OPEN
  • ***
  • Сообщений: 116
  • Карма: 8
Не смог повторить, но хотелось бы разобраться. Пробовал на версии Revit 2018.2 (Попробуйте обновление поставить). Тестировал на на DuctFitting.

Цитировать
/// -> Созданные объекты исчезли!!!
Что именно вы имеете ввиду? исчезли из чертежа совсем?

Оффлайн Алексей Кузин

  • ADN OPEN
  • ***
  • Сообщений: 116
  • Карма: 8
Вроде повторил. Притом именно на балках (но странно).
Обработчик события

Код - C# [Выбрать]
  1.         public static void DocumentSavingAs_EventHandler(object sender, DocumentSavingAsEventArgs e)
  2.         {
  3.             Document doc = e.Document;
  4.             SaveDocumentObjectIds(doc);
  5.         }


Мне помогло если я документ забираю из sender, который является Application. Видимо в аргументе документ прилетает левый, хотя очень странно.

Код - C# [Выбрать]
  1.             var app = sender as Autodesk.Revit.ApplicationServices.Application;
  2.             if (app == null)
  3.                 return;
  4.  
  5.             var uiApp = new UIApplication(app);
  6.  
  7.             if (uiApp?.ActiveUIDocument?.Document == null)
  8.                 return;
  9.             if (uiApp.ActiveUIDocument.Document.IsFamilyDocument)
  10.                 return;
  11.  
  12.             var doc = uiApp.ActiveUIDocument.Document;


Оффлайн Кирилл ЗахаровАвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 119
  • Карма: 5
Спасибо, Алексей.
У меня это не работает.
Сейчас еще попробую обновить Revit до версии 2018.2

Оффлайн Кирилл ЗахаровАвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 119
  • Карма: 5
if (uiApp?.ActiveUIDocument?.Document == null)
                return;
Никогда не видел такой записи проверки на null. Очень удобно)

Оффлайн Кирилл ЗахаровАвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 119
  • Карма: 5
Нет, это не работает, независимо от наличия обновления Revit.

Я еще раз прикладываю тестовый проект к этому сообщению. Я добавил в него две команды - по одной на каждый из тестовых сценариев. В классе Command2 команда, которая приводит к исчезновению объектов. Везде есть комментарии.

Оффлайн Алексей Кузин

  • ADN OPEN
  • ***
  • Сообщений: 116
  • Карма: 8
Посмотрю обязательно в понедельник. У меня были проблемы что не всегда удавалось повторить пропадания но ошибку я видел. Мне сейчас очень близка эта тема сохранения информации, занимаюсь этим активно и хочу разобраться. Кстати я привязывался к событиям немного по другому:

там где ваш код
Код - C# [Выбрать]
  1. a.ControlledApplication.ApplicationInitialized += OnApplicationInitialized;


я делал привязку к событиям сохранения. (Но не думаю что в этом дело.)

Код - C# [Выбрать]
  1.                 a.ControlledApplication.DocumentSaving += new EventHandler<Autodesk.Revit.DB.Events.DocumentSavingEventArgs>(documentSaving);
  2.                 a.ControlledApplication.DocumentSavingAs += new EventHandler<Autodesk.Revit.DB.Events.DocumentSavingAsEventArgs>(documentSavingAs);

Оффлайн Алексей Кузин

  • ADN OPEN
  • ***
  • Сообщений: 116
  • Карма: 8
Цитировать
Никогда не видел такой записи проверки на null. Очень удобно)
Очень упрощает код. так же проверку можно свести к такому виду:

Код - C# [Выбрать]
  1.             if (uiApp?.ActiveUIDocument?.Document?.IsFamilyDocument != false)
  2.                 return;

Оффлайн Кирилл ЗахаровАвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 119
  • Карма: 5
Я заметил еще одну особенность: проблема возникает только в том случае, когда при сохранении с новым именем, мы перезаписываем уже существующий файл.
Если до этого в данном расположении еще не было файла, то проблема не возникает.

Оффлайн Кирилл ЗахаровАвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 119
  • Карма: 5
Сразу добавлю, что удалить существующий файл в обработчике DocumentSavingAs не получается. Этот файл уже заблокирован.
Ревит меняет имя старого файла, дописывая суффикс с номером и записывает новый файл с именем старого

Оффлайн Кирилл ЗахаровАвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 119
  • Карма: 5
Мне пока что пришло в голову только одно решение: запретить сохранение с перезаписью старого файла
Код - C# [Выбрать]
  1.         public static void DocumentSavingAs_EventHandler(object sender, DocumentSavingAsEventArgs e)
  2.         {
  3.             if (!File.Exists(e.PathName))
  4.             {
  5.                 Document doc = e.Document;
  6.                 SaveDocumentObjectIds(doc);
  7.             }
  8.             else if(e.Cancellable)
  9.             {
  10.                 TaskDialog.Show("ПРОЕКТ НЕ СОХРАНЕН!", "Для сохранения проекта с именем "
  11.                     + e.PathName + " необходимо вручную удалить или переименовать существующий"+
  12.                     " файл с данным именем.");
  13.                 e.Cancel();
  14.             }
  15.         }
  16.  

Но в документации написано, что это событие может быть неотменяемым в том случае, если оно вызывается в момент закрытия проекта.

Оффлайн Кирилл ЗахаровАвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 119
  • Карма: 5
Autodesk уже зафиксировал эту проблему - https://forums.autodesk.com/t5/revit-api-forum/using-ext-storage-inside-documentsavingas-event-handler-causes/m-p/7783698/highlight/true#M28704. Но когда она будет решена неизвестно.

Оффлайн Алексей Кузин

  • ADN OPEN
  • ***
  • Сообщений: 116
  • Карма: 8
У меня так же повторяется как и у вас. Будет ждать ответа.
Кстати если привязаться к обработчику DocumentSavedAs всё сохраняется нормально. Но после команды SaveAs документ получится что изменен. Это не решение, но возможно вам это поможет... Хотя думаю вы сами это пробовали.

Оффлайн Кирилл ЗахаровАвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 119
  • Карма: 5
Кстати если привязаться к обработчику DocumentSavedAs всё сохраняется нормально.
Ну мне необходимо записывать данные в ProjectInfo именно перед сохранением проекта, а не после. Это основа всей логики моего разрабатываемого плагина.

Оффлайн Алексей Кузин

  • ADN OPEN
  • ***
  • Сообщений: 116
  • Карма: 8
Цитировать
Ну мне необходимо записывать данные в ProjectInfo именно перед сохранением проекта, а не после. Это основа всей логики моего разрабатываемого плагина.
Я так и понял. Просто думал на тему - записать данные после команды SaveAs а потом вызвать еще раз метод сохранения. Но из этого обработчика события точно не получится, просто для размышления...