Long Transactions are defensive against common problems resulting from objects that should not be cloned, or inter-object references that are not handled. If the LongTransactionManager (LTM) finds that it must clone a filtered class object to complete a long transaction checkOut() or checkIn(), it will abort the entire operation. If it finds an AcDbSoftPointerId or AcDbHardPointerId that is not in the cloning IdMap, it will also abort.
Applications that need to prevent their objects from being included as cloned in long transactions need to register those objects using the AcApLongTransactionManager::addClassFilter() function. AcDbProxyEntity and AcDbProxyObject are always filtered, so when the application is not present, all of its objects will be filtered automatically.
Wblock cloning handles all hard pointer references, but deep cloning does not require either type of reference to be mapped. Both of these cloning types are used in long transactions, depending on the type of transaction it is. If an application uses either of these types of references, or xdata handles, then its objects will be rejected from long transactions, unless the application takes extra steps to handle the references. This means that if the application is not loaded, then its objects and references will automatically be prevented from participating in long transactions and any data should be preserved in its absence.
Use the long transaction and deep clone notifications to intercept the cloning of their object and references, and to add whatever object cloning or mapping is needed. See the deep clone notification documentation and samples for more information on this.
If an object with a soft pointer reference is cloned (or a hard pointer reference in deep clone), the application must make sure that the reference ID is in the IdMap, either as a mapped ID pair, or a cloned ID pair. Mappings are usually used when objects refer to some common dictionary that the application maintains within the drawing. In deep clone, the mapping may consist of an IdPair where the key == value. In wblock clone between drawings, the IdPair would map one database's dictionary with the other database's dictionary. The reference is cloned by the application using either deepClone() or wblockClone() from the notification callback.
Taking these steps will guarantee “transitive closure.” To ensure that a set of objects that refer to each other can be checked out, and then checked back in again, without breaking the object's relationships, all associated objects are checked out together. For example, if any boundary or the associative hatch itself is passed into checkOut(), the hatch code adds all of the boundary objects to the list of objects to be checked out. This is how it achieves transitive closure. If this did not happen, the LTM would find the hatch's soft pointer IDs to its boundaries. If it found that a boundary object so referenced was missing from the cloning, the long transaction would be aborted. If it did not do this, even if no changes were made to the checked out hatch, the original hatch would lose its associativity on check in.
Sometimes, there are known references that do not need to be resolved. One situation would be an object that keeps track of all the entities that use it. For example, block table records keep a list of all the block references that use them. It is correct to only check out one of the references, so you must let the long transaction mechanism know that the rest of the references do not need to be cloned. There are several ways this can be done.
Here are some examples:
idMap.assign(idPair(id, AcDbObjectId::kNull, kFalse);
If the object needs to be cloned later, the idPair will be changed accordingly.
beginWblockClone(..., AcDbIdMapping& idMap) { ... AcDbDictionaryIterator* pIter = pDict->newIterator(); AcDbObject* pObj; for ( ; !pIter->done(); pIter->next()) { acdbOpenObject(pObj, pIter->objectId(), kForRead); AcDbVoidPtrArray* pReactors = pObj->reactors(); void* pReactor; AcDbObjectId rId; MyReactor* pMyReactor; if (pReactors) { for (int i = 0; i < pReactors->length(); i++) { pReactor = pReactors->at(i); if (acdbIsPersistentReactor(pReactor)) { rId = acdbPersistentReactorObjectId(pReactor); if (acdbOpenObject(pMyReactor, rId, kForRead) == eOk) { pMyReactor->close(); AcDbIdPair idPair(rId, AcDbObjectId::kNull, kFalse); idMap.assign(idPair); } } } } pObj->close(); } delete pIter; pDict->close(); }