Управление сериализацией объектов в MultiCAD.NET



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


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

    image

    Структура класса данного объекта выглядит следующим образом:

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

    Допустим, что в новой версии класса, потребовалось задавать угловые точки метки не точками, а векторами, поле radius при этом остается без изменений:

    [CustomEntityAttribute("1C925FA1-842B-49CD-924F-4ABF9717DB62", 3, "Crossmark", "Crossmark Sample Entity")]
    [Serializable]
    public class CrossMark : McCustomBase
    {
      private Vector3d v1;
      private Vector3d v2;
      private Vector3d v3;
      private Vector3d v4;
      private double radius;
    }
    

    Очевидно, что в результате изменений такого рода объекты нового класса не будут обладать совместимостью с предыдущей версией.
    Для того, чтобы новая версия смогла «понимать» предыдущую, необходимо реализовать механизм чтения необходимых полей и «перевод» старого формата данных в новый. Для решения этой задачи в MultiCAD.NET может быть использован стандартный интерфейс ISerializable, который позволяет осуществлять контролируемую сериализацию объектов.

    Для реализации ISerializable необходима имплементация двух методов:
    • public void GetObjectData(SerializationInfo info, StreamingContext context) — используется для сериализации объекта.
    • public CrossMark(SerializationInfo info, StreamingContext ctx) — конструктор, использующийся для десериализации.

    Для нашего случая эти методы будут выглядеть следующим образом:
    // Serialization
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
      info.AddValue("vec1", v1);
      info.AddValue("vec2", v2);
      info.AddValue("vec3", v3);
      info.AddValue("vec4", v4);
      info.AddValue("radius", radius);
    }
    

    // Deserialization
    public CrossMark(SerializationInfo info, StreamingContext ctx)
    {
      radius = info.GetDouble("radius");
      try
      {
        v1 = (Vector3d)info.GetValue("vec1", typeof(Vector3d));
        v2 = (Vector3d)info.GetValue("vec2", typeof(Vector3d));
        v3 = (Vector3d)info.GetValue("vec3", typeof(Vector3d));
        v4 = (Vector3d)info.GetValue("vec4", typeof(Vector3d));
      }
      catch (System.Runtime.Serialization.SerializationException)
      {
        Point3d pnt1 = (Point3d)info.GetValue("pnt1", typeof(Point3d));
        Point3d pnt2 = (Point3d)info.GetValue("pnt2", typeof(Point3d));
        Point3d pnt3 = (Point3d)info.GetValue("pnt3", typeof(Point3d));
        Point3d pnt4 = (Point3d)info.GetValue("pnt4", typeof(Point3d));
        
        v1 = pnt1.GetAsVector();
        v2 = pnt2.GetAsVector();
        v3 = pnt3.GetAsVector();
        v4 = pnt4.GetAsVector();
      }
    }
    

    Объект теперь имеет два конструктора: один из них используется при вставке объекта в чертеж, второй — при чтении объекта из чертежа. Процесс десериализации разделен на две части: чтение данных из объекта текущей версии происходит в «штатном» режиме, а данные объектов предыдущей версии зачитываются и приводятся к текущему формату в секции обработки исключения SerializationException, которое будет выброшено при попытке десериализации несуществующих полей из предыдущей версии.
    Если планируется дальнейшее развитие версий, то альтернативным вариантом может быть сериализация версии объекта в новое поле, и разделение процесса десериализации в зависимости от значения этого поля:

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
      ...
      info.AddValue("version", 1);
    }
    

    public CrossMark(SerializationInfo info, StreamingContext ctx)
    {
      int version = info.GetInt("version");
      switch (version)
      {
        case 1:
            ...
        case 2:
            ...
        ...    
    }
    


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

    Обсуждение статьи доступно также и на нашем форуме: forum.nanocad.ru/index.php?showtopic=6516.
    Перевод статьи на английский: Managing serialization of custom objects in MultiCAD.NET.
    Нанософт
    37.61
    Company
    Share post

    Comments 0

    Only users with full accounts can post comments. Log in, please.