Hi everyone,
I'm currently working on a program where I need to associate two entities (xdata or xrecord). I have two options: either store the handler of each object within the other, or store the object ID. My question is, what is the better choice?
Thank you.
Solved! Go to Solution.
Solved by _gile. Go to Solution.
Solved by _gile. Go to Solution.
Solved by jabowabo. Go to Solution.
https://help.autodesk.com/view/OARX/2022/ENU/?guid=GUID-8D56532D-2B17-48D1-8C81-B4AD89603A1C
"Handles are persistent between AutoCAD sessions, so they are the best way of accessing objects if you need to export drawing information to an external file which might later need to be used to update the drawing. The ObjectId of an object in a database exists only while the database is loaded into memory. Once the database is closed, the ObjectId assigned to an object no longer exist and maybe different the next time the database is opened."
Thank you for your response. So, the ObjectID, which is supposed to update with each AutoCAD session opening, does not work in my case.
@youssefGC wrote:
Thank you for your response. So, the ObjectID, which is supposed to update with each AutoCAD session opening, does not work in my case.
ObjectIds don't necessarily change between sessions - but they can. Handles do not. Therefore, using the Handle is better practice.
Depending on the context, identifiers stored in xdata (in this case Handle) may or may not be translated during cloning. And I think you can't presume to assume that they will be translated.
So I conclude that relationships between objects should be stored in xrecords whenever possible.
@youssefGC a écrit :
Thank you for your response. So, the ObjectID, which is supposed to update with each AutoCAD session opening, does not work in my case.
If you store the ObjectId as Soft-pointer (DXF 330), Hard-pointer (DXF 340), Soft-owner (DXF 350) or Hard-owner (DXF 360), the ObjectId will be automatically converted onto an Handle when the drawing is closed and automatically re-assigned when re-opening.
Your question opens a very big can of worms.
For example, what should happen when only one of two linked entities is copied, erased, arrayed, etc. You need to answer this question for both the referencing entity, and the referenced entity.
What should happen when both linked entities are copied?
The answer to how to best establish a persistent, inter-object reference depends almost entirely on the answers to these questions, which you may have not given much thought to, but definitely need to.
@jabowabo wrote:
ObjectIds don't necessarily change between sessions - but they can. Handles do not. Therefore, using the Handle is better practice.
Hate to disagree, but AutoCAD does not perform translation of handles in all cases. For example, given object 'A' which references object 'B', copy both A and B in the same operation,. Usually, the desired/expected behavior is that the copy of A will reference the copy of B.
With handles, that doesn't always happen. It requires ObjectIds stored in an Xrecord using hard/soft owner/pointer references, and the Xrecord must have its XLateReferences property set to true.
You may be have misinterpreted the OPs question as being about external references to objects in a drawing, verses internal references. External references (e.g., storing a reference to an object in a SQL table) can't be done using ObjectIds. It requires both a handle and a file identifier. Intra-drawing references (between different objects within the same drawing) only requires an ObjectId, and handles are not suitable for that, for the reasons mentioned above.
About the question of whether ObjectIds change between sessions. ObjectIds always change between sessions, because they are effectively pseudo-pointers that are only valid in the current editing session. They can't be used to reference objects externally (for example, in a SQL table), but they can be used to reference other objects within the same drawing. In a subsequent editing session, their literal value may not be the same, but they will still reference the same object.
Thank you for your remarks. Indeed, I have already considered using ObjectOverrule (override the DeepClone method so that the new entity is not attached to the old one ...).
However, the real issue currently blocking me (as I mentioned here and you can follow the discussion) concerns cases where the user does not load my code, and if they copy/paste one of the two entities, the resulting entity remains linked to the old one.
In case the plug-in does not load, there is no solution that guarantees it will load, but you can prevent it. What I do is load the plugin at startup (LoadOnAutoCADStartup = true). In case entities are manipulated without the plugin being loaded, I developed routines that check for association errors and give the user the option to correct them.
You are responsible for implementing the code about what should happen when DeepClone (copy, mirror, block, insert, array, etc.) or WblockClone (copyclip, cutclip, pasteclip, wblock, etc.) occurs.
DeepClone the result is immediately placed in the same database as the source.
WblockClone is the same as DeepClone, but allows you to clone to another database.
A soft pointer is simply a pointer to an object. It does not protect the referenced object from purge. If you use a soft pointer to refer to an object, you should check that the object still exists before you open it.
If you are going to store Hard/SoftPointerIds through XRecords in extension dictionaries, make sure that DBDictionary.TreatElementsAsHard = True. This ensures inclusion in WblockClone operations.
Useful content:
Advanced Deepclone API in AutoCAD by Cirylle Fauvel;
AutoCAD ObjectARX Developer's Guide;
Autodesk University 2010, CP230-1V - AutoCAD® .NET - Practical Examples of Customizing AutoCAD Entity Behavior.
There's no solution for cases where your code is not loaded. That's just the nature of the beast.
Not sure this exactly replies to your request, but it could be a good start (not deeply tested).
The following code uses extension methods from this library.
[CommandMethod("LINKENTITIES")]
public static void LinkEntities()
{
var options = new PromptEntityOptions("\nSelect first entity: ");
var result = Active.Editor.GetEntity(options);
if (result.Status != PromptStatus.OK) return;
var firstId = result.ObjectId;
options.Message = "\nSelect second entity: ";
result = Active.Editor.GetEntity(options);
if (result.Status != PromptStatus.OK) return;
var secondId = result.ObjectId;
LinkEntities(firstId, secondId);
}
[CommandMethod("GETLINKED")]
public static void GetLinkedEntity()
{
var options = new PromptEntityOptions("\nSelect source entity: ");
var result = Active.Editor.GetEntity(options);
if (result.Status != PromptStatus.OK) return;
var sourceId = result.ObjectId;
if (TryGetLinkedEntity(sourceId, out ObjectId targetId))
{
Active.Editor.SetImpliedSelection(new[] { sourceId, targetId });
}
}
private static void LinkEntities(ObjectId firstId, ObjectId secondId)
{
using (var tr = new OpenCloseTransaction())
{
var firstEntity = tr.GetObject(firstId, OpenMode.ForWrite);
var secondEntity = tr.GetObject(secondId, OpenMode.ForWrite);
firstEntity.SetXDictionaryXrecordData(tr, "LINK", new TypedValue(330, secondId));
secondEntity.SetXDictionaryXrecordData(tr, "LINK", new TypedValue(330, firstId));
tr.Commit();
}
}
private static bool TryGetLinkedEntity(ObjectId sourceId, out ObjectId targetId)
{
targetId = ObjectId.Null;
using (var tr = new OpenCloseTransaction())
{
var sourceEntity = sourceId.GetObject<Entity>(tr, OpenMode.ForRead);
if(sourceEntity.TryGetXDictionaryXrecordData(tr, "LINK", out ResultBuffer sourceData))
{
targetId = (ObjectId)sourceData.AsArray()[0].Value;
if (!targetId.IsErased)
{
var targetEntity = tr.GetObject(targetId, OpenMode.ForRead);
return
targetEntity.TryGetXDictionaryXrecordData(tr, "LINK", out ResultBuffer targetData) &&
(ObjectId)targetData.AsArray()[0].Value == sourceId;
}
}
return false;
}
}
Unfortunately, I can't find any other solution except using the NOD to store the handles of each link pair. However, I'm concerned about the performance impact on the document if the drawing contains many entities (resulting in many stored couples in the NOD).
If you are afraid of having performance problems, there is only one way to know and that is to try it.
I use ObjectOverrule and the maximum number of relations between objects that I tested was 2000 and I had no performance problems.
You always have the option of finding parts of code that cause delays and improving that. I think you will have more bug issues (unforeseen situations) to resolve than performance issues.
Can't find what you're looking for? Ask the community or share your knowledge.