Почему в AutoCAD методы не должны возвращать объекты отложенных запросов при использовании Transaction

    Рассмотрим две реализации одного и того же метода: в первой будет возвращаться IEnumerable<T>, являющийся результатом работы отложенного LINQ-запроса, а во второй реализации - Array.

Первая реализация

   1:  /// <summary>
   2:  /// Получить все табличные стили текущего чертежа
   3:  /// </summary>
   4:  /// <returns>Возвращается перечень объектов TableStyle</returns>
   5:  IEnumerable<TableStyle> GetTableStylesFromDrawing1()
   6:  {
   7:      Document dwg = acadApp.DocumentManager.MdiActiveDocument;
   8:      Database db = dwg.Database;
   9:   
  10:      using (Transaction trs = dwg.TransactionManager.StartTransaction())
  11:      {
  12:          DBDictionary tableStylesDict = (DBDictionary) trs.GetObject(db.TableStyleDictionaryId, OpenMode.ForRead);                
  13:          return tableStylesDict.Cast<DictionaryEntry>().Select(n => (TableStyle) trs.GetObject( (ObjectId)n.Value, OpenMode.ForRead));
  14:      }            
  15:  }

Вторая реализация

   1:  /// <summary>
   2:  /// Получить все табличные стили текущего чертежа
   3:  /// </summary>
   4:  /// <returns>Возвращается массив объектов TableStyle</returns>
   5:  TableStyle[] GetTableStylesFromDrawing2()
   6:  {
   7:      Document dwg = acadApp.DocumentManager.MdiActiveDocument;
   8:      Database db = dwg.Database;
   9:   
  10:      using (Transaction trs = dwg.TransactionManager.StartTransaction())
  11:      {
  12:          DBDictionary tableStylesDict = (DBDictionary) trs.GetObject(db.TableStyleDictionaryId, OpenMode.ForRead);                
  13:          return tableStylesDict.Cast<DictionaryEntry>().Select(n => (TableStyle) trs.GetObject( (ObjectId)n.Value, OpenMode.ForRead)).ToArray();
  14:      }            
  15:  }

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

1. Пишем команду, которая будет использовать реализацию первого метода:

   1:  [CommandMethod("q1")]
   2:  public void Test1()
   3:  {
   4:      Document doc = acadApp.DocumentManager.MdiActiveDocument;
   5:      Editor ed = doc.Editor;
   6:      Database db = doc.Database;
   7:      IEnumerable<TableStyle> tss = GetTableStylesFromDrawing1();
   8:      ed.WriteMessage(string.Format("\nКоличество табличных стилей в чертеже: {0}\n", tss.Count()));
   9:      foreach (TableStyle item in tss)
  10:      {
  11:          ed.WriteMessage(string.Format("{0}\n", item.Name));
  12:      }
  13:  }

Результат работы этой команды таков:



2. Пишем команду, которая будет использовать реализацию второго метода:

   1:  [CommandMethod("q2")]
   2:  public void Test2()
   3:  {
   4:      Document doc = acadApp.DocumentManager.MdiActiveDocument;
   5:      Editor ed = doc.Editor;
   6:      Database db = doc.Database;
   7:      TableStyle[] tss = GetTableStylesFromDrawing2();
   8:      ed.WriteMessage(string.Format("\nКоличество табличных стилей в чертеже: {0}\n", tss.Count()));
   9:      foreach (TableStyle item in tss)
  10:      {
  11:          ed.WriteMessage(string.Format("{0}\n", item.Name));
  12:      }
  13:  }

Результат работы этой команды будет таков:

Причина возникновения Fatal Error

    Я думаю, что причина фатальной ошибки, получаемой при использовании первого метода, заключается в том, что отложенный запрос запускается на исполнение и использует объект Transaction, который к тому времени уже на самом деле прекратил своё существование и был уничтожен сборщиком мусора.

Вывод

    Т.о. методы, используемые в работе плагинов AutoCAD не должны возвращать объекты отложенных операций, полученных с помощью объекта Transaction.

Comments