Pull to refresh
101.79
Нанософт разработка
Инженерная экосистема

Сериализация объектов в MultiCAD.NET. Управление совместимостью чертежей и прокси-объектами

Reading time 4 min
Views 3.1K

При создании пользовательских объектов на традиционном C++ API (NRX в nanoCAD, ObjectARX в AutoCAD) для обеспечения сохранения объектов и чтения их из файла чертежа необходимо в явном виде описывать запись (сериализацию) и чтение (десериализацию) каждого поля. В MultiCAD.NET API применён более привычный .NET разработчикам описательный подход, в основе которого лежит стандартная .NET сериализация.

Применение сериализации, нечувствительной к версии объектов (Version Tolerance Serialization), предоставляет разработчикам более гибкий механизм управления совместимостью объектов разных версий, чем существующий в традиционном C++ API, где предусмотрено чтение предыдущих версий, но чтение файлов «из будущего» невозможно.

В MultiCAD.NET при описании новых версий объектов можно указать, что вновь добавленные поля необязательны, и тогда чертёж, сохранённый в формате новой версии приложения, прочтётся и в предыдущей версии. Разумеется, без изменений остался и традиционный подход, приводящий к созданию прокси объектов (кешированной графики объектов) при загрузке чертежа в предыдущую версию приложения.

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


Давайте рассмотрим как MultiCAD.NET использует оба этих подхода (VTS и механизм proxy-объектов) для организации управления совместимостью объектов. В качестве наглядного примера рассмотрим две версии пользовательского объекта «Крестовая метка».



Создание класса для экспериментов


Об основах создания пользовательских примитивов в MultiCAD.NET мы рассказывали в одной из прошлых статей, поэтому не будем подробно на этом останавливаться, а сразу перейдём к описанию пользовательского примитива «Крестовая метка»:

[CustomEntity("1C925FA1-842B-49CD-924F-4ABF9717DB62", 1, "Crossmark", "Crossmark Sample Entity")]
[Serializable]
public class CrossMark : McCustomBase
{
  private Point3d pnt1;
  private Point3d pnt2;
  private Point3d pnt3;
  private Point3d pnt4;
}

Полный код приложения доступен здесь. После компиляции приложения и запуска его командой «crossmark» по результату пользовательского ввода на чертеж будет добавлен следующий примитив:

image

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

Вторая версия класса и обеспечение межверсионной совместимости объектов


Во второй версии класса мы изменим геометрию нашего примитива, добавив окружность в центр крестовой метки. В качестве дополнительного поля добавим радиус этой окружности, снабдив его атрибутом [OptionalField], чтобы отметить его, как необязательное в случае десериализации более ранней версии объекта. Укажем также и номер новой версии, используюя свойство VersionAdded:

[OptionalField(VersionAdded = 2)]
private double radius = -1;

Инициализируем новую переменную в конструкторе и добавим общедоступное свойство для получения доступа к этому полю:

public CrossMark() 
{
  ...
  radius = 10;
}

[Category("Circular component")]
[DisplayName("Circle radius")]
[Description("Radius of circular component of the cross mark")]
public double Radius
{
  get
  {
    return radius;
  }
  set
  {
    radius = value;
  }
}

Для того, чтобы проинициировать добавленнное поле после десериализации объекта правильным значением, будет использоваться метод OnDeserialized с атрибутом [OnDeserialized]:

[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
  if (radius == -1)
  {
    radius = 10;
  }
}


Теперь приложения, работающие с новыми версиями типа, могут также принимать объекты более старых версий. Кроме этого, VTS также решает проблему обратной совместимости: приложения, работающие со старыми версиями объектов могут принимать и новые версии: «лишние» поля при этом просто игнорируются и десериализация проходит успешно.

Таким образом, мы обеспечили полную совместимость версий нашего класса. Осталось только добавить отрисовку окружности в метод OnDraw():

public override void OnDraw(GeometryBuilder dc)
{
  ...
  dc.DrawCircle(new Point3d(pnt1.X + 25, pnt1.Y, pnt1.Z), radius);
}

Полный код второй версии приложения также доступен в архиве. Компилируем проект, запускаем nanoCAD, загружаем построенную сборку и запускаем новую версию приложения все той же командой «crossmark». После указания точки вставки получаем .dwg файл с обновленной версией нашего примитива:

image

Так же сохраняем полученный файл.

Результаты

А теперь убедимся в полной совместимости обеих версий.

Запускаем nanoCAD, загружаем вторую версию сборки и открываем первую версию файла. Файл будет успешно открыт и на экране нас ожидает сохраненный в первой версии примитив. Как только будет произведено какое-либо действие, приводящие к перерисовке объекта (например, перемещение) объект будет перерисован в новой версии с учетом работы конструктора сериализации: крестовая метка будет нарисована с окружностью заданного радиуса. Для этого также может быть использована команда REGENOBJ.
Закрываем файл. Загружаем первую версию приложения и открываем вторую версию файла. Файл также успешно загрузится и первоначально будет отрисован объект второй версии, как он был сохранен в файле. После же перерисовки крестовая метка будет изображена в том виде, который был определен в первой версии.

Традиционный подход. Совместимость сверху вниз


Механизм VTS, позволяющий обеспечить совместимость снизу вверх, применим далеко не всегда, поскольку не все данные в новых версиях объектов могут быть сочтены несущественными. Для того, чтобы обеспечить совместимость сверху вниз, при создании новой версии объекта необходимо использовать атрибут следующего вида:

[CustomEntity(guid, majorVersion, databaseName, localName)]

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

Заключение


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

Обсуждение статьи доступно также и на нашем форуме: forum.nanocad.ru/index.php?showtopic=6515.
Перевод статьи на английский: Serializing objects in MultiCAD.NET. Managing drawings compatibility and proxy objects.
Tags:
Hubs:
+9
Comments 0
Comments Leave a comment

Articles

Information

Website
www.nanocad.ru
Registered
Founded
Employees
Unknown
Location
Россия