Serializing objects in MultiCAD.NET. Managing drawings compatibility and proxy objects

serialization_04

Creating custom objects using a traditional C++ API (NRX in NanoCAD and ObjectARX in AutoCAD) presupposes to implement serialization and deserialization processes of each field to allow objects to be writable and readable. The MultiCAD.NET API provides a simple and compact descriptive approach based on the standard .NET serialization that is more usual for .NET developers.

The version tolerance serialization features (VTS) provides developers with a mechanism for managing object versions compatibility which is more flexible than the traditional C++ API approach that allows reading of previous object versions (backward compatibility) but not reading files “from the future” (forward compatibility).

When newer object versions are being defined in MultiCAD.NET, newly created fields can be marked as optional fields so that the drawing saved in a new format version can be read in the previous version also. Of course, the API contains an ability to create proxy objects (cached graphics data of the objects) if the drawing is being loaded to the previous version of the application.

In this post we are going to discuss how to ensure compatibility of two object versions and how to provide the traditional level of compatibility when newer versions of the application can read older drawings but older versions are unable to read new drawings.

Let’s consider how MultiCAD.NET uses both these approaches (VTS and proxy objects mechanism) for providing the versions compatibility features. We will use two versions of the “cross mark” entity for demonstration:

serialization_05

The initial class version

The definition of the CrossMark entity class is listed below:

We are not going to focus on the basics of creating custom objects; it has already been discussed in the previous post. The entire code is available here. After the application is compiled and loaded, it can be launched by the “crossmark” command. The following entity will be added to the drawing:

serialization_02

Save the drawing. Now let’s change the class by adding an extra functionality to it.

The second version of the class and providing versions compatibility

We are going to change the entity’s geometry in the second version by adding a circle at the center of the cross mark. Add a field for the radius of the circle and use the [OptionalField] attribute to mark it as an optional field for correct deserialization of the previous version. Also specify the new version number in the VersionAdded property:

Set the proper value of the radius in the constructor and add a public property for accessing the field:

To initialize the radius field with the custom value after deserialization, the method OnDeserialized with the [OnDeserialized] attribute can be used:

In that way applications that work with new versions of object can also process objects of older versions. Also VTS solves the problem of forward compatibility, i.e. applications that work with old object versions are able to read new versions as well: fields marked as optional are ignored and the object is deserialized properly.
So, we have provided full compatibility between both versions of the CrossMark class. The only thing that should be done is to add a code for drawing the circle component to the OnDraw() method:

The full code of the second version of the application is also available in this archive. Compile the project, load the built assembly to nanoCAD and launch the application by the “crossmark” command. Since you specify the insertion point, the new version of the crossmark entity is drawn at this point:

serialization_03

Save the drawing with a new name.

Testing and results

We have two drawings with two versions of the entity. Let’s make sure that both versions are compatible.
Run nanoCAD, load the second version of the assembly and open the first file. The file is successfully opened and the primitive of the first version is displayed. Once object redrawing is called (for example while you are moving object) the object is redrawn in the new version with the circle component of the proper radius. Object redrawing can also be initialized by the REGENOBJ command.
Close the file. Load the first version of the assembly and open the second file. The file is also successfully opened and the primitive of the second version is displayed as it was originally saved in the drawing (with the circle component). After selecting the REGENOBJ command (or any user actions that cause updating) the cross mark geometry will be updated and displayed as it was defined in the first version.

The traditional approach. Proxy objects

The VTS mechanism, which allows forward compatibility, is not always applicable. For example, when not all data specific for new versions, can be considered as insignificant and you want to prevent changing new objects while opening in an older version of the application.
As mentioned above the class definition contains the [CustomEntity] attribute with a set of properties:

The majorVersion property defines the major version number within which the objects can be compatible. If new objects constructed with a majorVersion value, that is greater than the previous one, are being opened in the older application version, the eMakeMeProxy code is returned. In this case such objects will be displayed as proxy objects That precludes them from changing and being editable.
For example, the second version of the cross mark object should be defined with a majorVersion value equal 2:

Conclusion

In this post we have discussed an example of providing compatibility between two versions of the custom object in the simplest case when the new version is created from the previous one by adding new fields. In one of the future posts we will talk about managing compatibility in the case of deeper changes such as deleting, renaming object fields or changing their types.

Leave a Reply