Author Topic: Writing Dictionary to NOD causes error.  (Read 2231 times)

0 Members and 1 Guest are viewing this topic.

Atook

  • Swamp Rat
  • Posts: 1029
  • AKA Tim
Writing Dictionary to NOD causes error.
« on: November 25, 2015, 12:04:08 PM »
Hey gang.

Writing xrecords to a dictionary on the NOD. The code seems to run fine, the dictionary gets created the first time through, and xrecords seem to get added to it properly. I'm using a locked transaction.

But when I try to save I'm getting the error: One or more objects in this drawing cannot be saved to the specified format.
If I audit the drawing after creating the dictionaries, then saving seems to work fine, though no errors are found.

I figure I'm missing closing something up, or something along those lines.

My dictionary code is:
Code - C: [Select]
  1. public static void AddXrecordToNamedDictionary(string dictionaryName, string key, Xrecord xRec)
  2. {
  3.         DBDictionary myDbDictionary = new DBDictionary();
  4.         using (LockedTransaction tr = Active.Document.TransactionManager.StartLockedTransaction())
  5.         {
  6.                 DBDictionary nod = tr.GetObject(HostApplicationServices.WorkingDatabase.NamedObjectsDictionaryId,
  7.                         OpenMode.ForWrite) as DBDictionary;
  8.                 if (nod.Contains(dictionaryName))
  9.                 {
  10.                        // set the dictionary
  11.                         myDbDictionary = (DBDictionary)tr.GetObject(nod.GetAt(dictionaryName), OpenMode.ForWrite);
  12.                 }
  13.                 else
  14.                 {
  15.                         // create the dictionary
  16.                         nod.SetAt(dictionaryName, myDbDictionary);
  17.                         tr.AddNewlyCreatedDBObject(myDbDictionary, true);
  18.                 }
  19.                 myDbDictionary.SetAt(key, xRec);
  20.                 tr.Commit();
  21.         }
  22. }
« Last Edit: November 25, 2015, 12:09:46 PM by Atook »

Atook

  • Swamp Rat
  • Posts: 1029
  • AKA Tim
Re: Writing Dictionary to NOD causes error.
« Reply #1 on: November 25, 2015, 08:43:28 PM »
Turns out the problem was not adding the new xrecord to the database transaction.

Corrected code:
Code - C: [Select]
  1. public static void AddXrecordToNamedDictionary(string dictionaryName, string key, Xrecord xRec)
  2. {
  3.         DBDictionary myDbDictionary = new DBDictionary();
  4.         using (LockedTransaction tr = Active.Document.TransactionManager.StartLockedTransaction())
  5.         {
  6.                 DBDictionary nod = tr.GetObject(HostApplicationServices.WorkingDatabase.NamedObjectsDictionaryId,
  7.                         OpenMode.ForWrite) as DBDictionary;
  8.                 if (nod.Contains(dictionaryName))
  9.                 {
  10.                         // set the dictionary
  11.                         myDbDictionary = (DBDictionary)tr.GetObject(nod.GetAt(dictionaryName), OpenMode.ForWrite);
  12.                 }
  13.                 else
  14.                 {
  15.                         // create the dictionary
  16.                         nod.SetAt(dictionaryName, myDbDictionary);
  17.                         tr.AddNewlyCreatedDBObject(myDbDictionary, true);
  18.                 }
  19.                 // add the xrecord
  20.                 myDbDictionary.SetAt(key, xRec);
  21.                 tr.AddNewlyCreatedDBObject(xRec,true);//<--This was missing, causing the error
  22.                 tr.Commit();
  23.         }
  24. }

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: Writing Dictionary to NOD causes error.
« Reply #2 on: November 25, 2015, 08:50:36 PM »
excellent result !
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

huiz

  • Swamp Rat
  • Posts: 917
  • Certified Prof C3D
Re: Writing Dictionary to NOD causes error.
« Reply #3 on: November 26, 2015, 03:08:55 AM »
You should test this with the following:

Doing an UNDO somewhere after you have added a record might result in a removed record. That is a bug (I guess, it might be a feature) and can be solved by trying to remove the key before you add it.

Code: [Select]
...
    try {
        myDbDictionary.Remove(key);
    } catch (System.Exception) { }
    myDbDictionary.SetAt(key, xRec);
...

The conclusion is justified that the initialization of the development of critical subsystem optimizes the probability of success to the development of the technical behavior over a given period.

n.yuan

  • Bull Frog
  • Posts: 348
Re: Writing Dictionary to NOD causes error.
« Reply #4 on: November 26, 2015, 09:42:26 AM »

   DBDictionary myDbDictionary = new DBDictionary();


The line in BOLD is not a good code: why do you create a dictionary before you are sure there is no existing dictionary? In your code following, if the NOD has the required dictionary, your code re-points the variable "myDbDictionary" to the existing one (opened in the transition) and effectively leaves the "newed" dictionary (created in the BOLD line of code) in momery without properly disposed.

You should:

DBDictionary myDbDictionary = null;

using (transansaction...())
{
    if (nod.Contains(...))
    {
        myDbDictionary=[open in transaction]
    }
    else
    {
        myDbDictionary=new DbDictionary(); //you only create the DBDictionary object when it is needed, and the instance will be handled by the transaction
    }
    ....
}

Atook

  • Swamp Rat
  • Posts: 1029
  • AKA Tim
Re: Writing Dictionary to NOD causes error.
« Reply #5 on: November 27, 2015, 12:24:45 AM »
.. UNDO somewhere after you have added a record might result in a removed record. That is a bug (I guess, it might be a feature) and can be solved by trying to remove the key before you add it..

Thanks for the tip huiz. I've implemented the remove in a try/catch as you suggested. But I'm trying to understand the problem. If the record has been removed, and I add it again, will it cause a problem? Does .SetAt throw an error for duplicate keys?  Is the feature/bug in the implementation of the .Remove method of the DBDictionary?

@n.yuan: But it is good code, it compiles! That means it's good right?!  :-D Thanks for the suggestion. You'll see I've implemented it, I've also moved the variable declaration inside of the using statement. For clarification, my understanding is that the initialized variable would be disposed of by GC after exiting scope, is this incorrect because it's used in a transaction?

Current Version:
Code - C: [Select]
  1. public static void AddXrecordToNamedDictionary(string dictionaryName, string key, Xrecord xRec)
  2. {
  3.         using (LockedTransaction tr = Active.Document.TransactionManager.StartLockedTransaction())
  4.         {
  5.                 DBDictionary nod = tr.GetObject(HostApplicationServices.WorkingDatabase.NamedObjectsDictionaryId,
  6.                         OpenMode.ForWrite) as DBDictionary;
  7.                 DBDictionary myDbDictionary = null;
  8.                 if (nod.Contains(dictionaryName))
  9.                 {
  10.                         // set the dictionary
  11.                         myDbDictionary = (DBDictionary)tr.GetObject(nod.GetAt(dictionaryName), OpenMode.ForWrite);
  12.                 }
  13.                 else
  14.                 {
  15.                         // create the dictionary
  16.                         myDbDictionary=new DBDictionary();
  17.                         nod.SetAt(dictionaryName, myDbDictionary);
  18.                         tr.AddNewlyCreatedDBObject(myDbDictionary, true);
  19.                 }
  20.  
  21.                 // try to remove the record to clear out a bugged removed record
  22.                 try
  23.                 {
  24.                         myDbDictionary.Remove(key);
  25.                 }
  26.                 catch (Exception){}
  27.                
  28.                 // add the xrecord
  29.                 myDbDictionary.SetAt(key, xRec);
  30.                 tr.AddNewlyCreatedDBObject(xRec,true);
  31.                 tr.Commit();
  32.         }
  33. }

n.yuan

  • Bull Frog
  • Posts: 348
Re: Writing Dictionary to NOD causes error.
« Reply #6 on: November 27, 2015, 09:57:01 AM »
Declaring variable and creating an instance of a class is not the same thing. The good practice for creating instance of a class is to only do it when needed.

Yes, normally, an instance of .NET class would be GCed automatically decided by the runtime (e.g our code usually does not need to explicitly call Dispose()). However, the classes in AutoCAD.NET API are not pure .NET objects. They are .NET wrappers of AutoCAD C/C++ objects. if not referenced object is properly GCed would depends on how the Dispose() is implemented in the .NET wrapper, which most of our Autodesk outside would have no idea. There had been a lot of discussions on if [AutoCAD object].Dispose() needs to be called or not during the first a few year after AutoCAD .NET API was released and the discussion still pops up now and then. The generally accepted approach is, any object your code created, not regulated by a Transaction needs to be disposed explicitly. You can search this forum and Autodesk's discussion forum in the topic of "to dispose or to not dispose".