.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

store vlaues with Xrecords

27 REPLIES 27
SOLVED
Reply
Message 1 of 28
youssefGC
1819 Views, 27 Replies

store vlaues with Xrecords

youssefGC
Advocate
Advocate

Hi forums,

 

Is there a method to store a pair of data values (dictionary < key, list<objectid>>) in once key in the Xrecords of an entity?

 

Thank you.

 

0 Likes

store vlaues with Xrecords

Hi forums,

 

Is there a method to store a pair of data values (dictionary < key, list<objectid>>) in once key in the Xrecords of an entity?

 

Thank you.

 

27 REPLIES 27
Message 2 of 28
norman.yuan
in reply to: youssefGC

norman.yuan
Mentor
Mentor

Xrecord's Data is a ResultBuffer, which is an array of TypedValue. So, you can have as many as TypedValues in the ResultBuffer (Xrecord.Data). To store a list of ObjectId, you do:

 

var values=from id in TheObjectIdList select new TypedValue((int)DxfCode.SoftPointerId, id);

var xrec = new Xrecord();

xrec.Data=new ResultBuffer(values.ToArray());

tran.AddNewlyCreatedObject(xrec, true);

TheDict.SetAt("theKey", xrec);

 

 

Norman Yuan

Drive CAD With Code

EESignature

Xrecord's Data is a ResultBuffer, which is an array of TypedValue. So, you can have as many as TypedValues in the ResultBuffer (Xrecord.Data). To store a list of ObjectId, you do:

 

var values=from id in TheObjectIdList select new TypedValue((int)DxfCode.SoftPointerId, id);

var xrec = new Xrecord();

xrec.Data=new ResultBuffer(values.ToArray());

tran.AddNewlyCreatedObject(xrec, true);

TheDict.SetAt("theKey", xrec);

 

 

Norman Yuan

Drive CAD With Code

EESignature

Message 3 of 28

ActivistInvestor
Advisor
Advisor

Xrecords can't store managed types like dictionaries directly, but you can store the keys and values as individual items. You could use a few helper methods like these:

 

public static class DictionaryToResultBufferExtensions
{ 
   public static ResultBuffer ToResultBuffer<TKey, TValue>(this Dictionary<TKey, TValue> items, short keyCode, short valueCode)
   {
      var result = new ResultBuffer();
      foreach(var pair in items) 
      {
         result.Add(new TypedValue(keyCode, pair.Key));
         result.Add(new TypedValue(valueCode, pair.Value));
      }
      return result;
   }

   public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this ResultBuffer rb)
   {
      var result = new Dictionary<TKey,TValue>();
      var e = rb.GetEnumerator();
      while(e.MoveNext()) 
      {
         var key = e.Current.Value;
         if(!e.MoveNext())
            throw new InvalidOperationException("Count mismatch");
         result.Add((TKey)key, (TValue)e.Current.Value);
      }
      return result;
   }
}

public static class Example
{
   [CommandMethod("CONVERTDEMO")]
   public static void Run()
   {
      Dictionary<string, double> map = new Dictionary<string, double>();
      map["First"] = 100.0;
      map["Second"] = 200.0;
      map["Third"] = 300.0;

      // Convert to ResultBuffer
      ResultBuffer rb = map.ToResultBuffer((short) DxfCode.Text, (short) DxfCode.Real);

      Write("\nResultBuffer: ");
      foreach(TypedValue tv in rb)
      {
         Write($"TypeCode: {tv.TypeCode} Value: {tv.Value}");
      }


      // Convert back to Dictionary<string, double>
      map = rb.ToDictionary<string, double>();

      Write("\nDictionary: ");
      foreach(var pair in map)
      {
         Write($"{pair.Key} = {pair.Value}");
      }

      static void Write(string fmt, params object[] args)
      {
         Application.DocumentManager.MdiActiveDocument?
            .Editor.WriteMessage("\n" + fmt, args);
      }
   }
}

 

Xrecords can't store managed types like dictionaries directly, but you can store the keys and values as individual items. You could use a few helper methods like these:

 

public static class DictionaryToResultBufferExtensions
{ 
   public static ResultBuffer ToResultBuffer<TKey, TValue>(this Dictionary<TKey, TValue> items, short keyCode, short valueCode)
   {
      var result = new ResultBuffer();
      foreach(var pair in items) 
      {
         result.Add(new TypedValue(keyCode, pair.Key));
         result.Add(new TypedValue(valueCode, pair.Value));
      }
      return result;
   }

   public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this ResultBuffer rb)
   {
      var result = new Dictionary<TKey,TValue>();
      var e = rb.GetEnumerator();
      while(e.MoveNext()) 
      {
         var key = e.Current.Value;
         if(!e.MoveNext())
            throw new InvalidOperationException("Count mismatch");
         result.Add((TKey)key, (TValue)e.Current.Value);
      }
      return result;
   }
}

public static class Example
{
   [CommandMethod("CONVERTDEMO")]
   public static void Run()
   {
      Dictionary<string, double> map = new Dictionary<string, double>();
      map["First"] = 100.0;
      map["Second"] = 200.0;
      map["Third"] = 300.0;

      // Convert to ResultBuffer
      ResultBuffer rb = map.ToResultBuffer((short) DxfCode.Text, (short) DxfCode.Real);

      Write("\nResultBuffer: ");
      foreach(TypedValue tv in rb)
      {
         Write($"TypeCode: {tv.TypeCode} Value: {tv.Value}");
      }


      // Convert back to Dictionary<string, double>
      map = rb.ToDictionary<string, double>();

      Write("\nDictionary: ");
      foreach(var pair in map)
      {
         Write($"{pair.Key} = {pair.Value}");
      }

      static void Write(string fmt, params object[] args)
      {
         Application.DocumentManager.MdiActiveDocument?
            .Editor.WriteMessage("\n" + fmt, args);
      }
   }
}

 

Message 4 of 28
youssefGC
in reply to: youssefGC

youssefGC
Advocate
Advocate

I'm sorry I didn't provide enough information about my case. In fact, I want to store in the NOD the ObjectID of the "parent" (entity 1 in the pic below) and the list of ObjectIDs of the "children" (entities 2 and 3). This relationship is stored as a dictionary: Dictionary<ObjectId Parent, List<ObjectId> children>. This allows me to access all entities related to a given parent afterwards and check if already exist (ContainsKey(TKey) Method). Since I have multiple relationships of this type in my design.

 

photo.png

 

The issue I'm encountering is storing this architecture at the Xrecords level.

 

I tried to implement an idea: I store all entities sequentially in an array of TypedValue, using a string separator "||" between each parent.

 

 

                ResultBuffer rsbf = new ResultBuffer(new TypedValue((int)DxfCode.SoftPointerId, ent1.ObjectId),
                    new TypedValue((int)DxfCode.SoftPointerId, ent2.ObjectId),
                    new TypedValue((int)DxfCode.SoftPointerId, ent3.ObjectId),
                    new TypedValue((int)DxfCode.Text, "||"), // separator
                    new TypedValue((int)DxfCode.SoftPointerId, ent1.ObjectId),
                    new TypedValue((int)DxfCode.SoftPointerId, ent2.ObjectId),
                    new TypedValue((int)DxfCode.SoftPointerId, ent3.ObjectId)
                    );

                try
                {
                    // Find the NOD in the database
                    DBDictionary nod = (DBDictionary)tr.GetObject(doc.Database.NamedObjectsDictionaryId, OpenMode.ForWrite);
                    // We use Xrecord class to store data in Dictionaries

                    if (nod.Contains(key)) // existing key
                    {
                        xRec = (Xrecord)tr.GetObject(nod.GetAt(key), OpenMode.ForWrite, false);
                        xRec.Data = rsbf;
                    }
                    else // new element
                    {
                        xRec.Data = rsbf;
                        nod.SetAt(key, xRec);
                        tr.AddNewlyCreatedDBObject(xRec, true);
                    }

                }
                catch
                 {
                    doc.Editor.WriteMessage("\nError");
                }

 

 

Read informations :

 

 

 try
                {
                    DBDictionary nod = (DBDictionary)tr.GetObject(doc.Database.NamedObjectsDictionaryId, OpenMode.ForRead);
                    Xrecord readBack = (Xrecord)tr.GetObject(nod.GetAt(key), OpenMode.ForRead);

                    ResultBuffer rs = readBack.Data;

                    if (rs != null)
                    {
                        TypedValue[] result = rs.AsArray();
                        bool isStartItem = true;

                        for (int i = 0; i < result.Length; i++)
                        {
                            if (result[i].TypeCode == (int)DxfCode.SoftPointerId)
                            {
                                if (isStartItem)
                                {
                                    ed.WriteMessage("\nParent element : " + (ObjectId)result[i].Value);
                                    isStartItem = false;
                                }
                                else
                                    ed.WriteMessage("\nChild element : " + (ObjectId)result[i].Value);
                            }
                            else if (result[i].TypeCode == (int)DxfCode.Text)
                                isStartItem = true;
                        }
                    }
                }
                catch
                {
                    doc.Editor.WriteMessage("\nError");
                }

 

 

 

 

 

0 Likes

I'm sorry I didn't provide enough information about my case. In fact, I want to store in the NOD the ObjectID of the "parent" (entity 1 in the pic below) and the list of ObjectIDs of the "children" (entities 2 and 3). This relationship is stored as a dictionary: Dictionary<ObjectId Parent, List<ObjectId> children>. This allows me to access all entities related to a given parent afterwards and check if already exist (ContainsKey(TKey) Method). Since I have multiple relationships of this type in my design.

 

photo.png

 

The issue I'm encountering is storing this architecture at the Xrecords level.

 

I tried to implement an idea: I store all entities sequentially in an array of TypedValue, using a string separator "||" between each parent.

 

 

                ResultBuffer rsbf = new ResultBuffer(new TypedValue((int)DxfCode.SoftPointerId, ent1.ObjectId),
                    new TypedValue((int)DxfCode.SoftPointerId, ent2.ObjectId),
                    new TypedValue((int)DxfCode.SoftPointerId, ent3.ObjectId),
                    new TypedValue((int)DxfCode.Text, "||"), // separator
                    new TypedValue((int)DxfCode.SoftPointerId, ent1.ObjectId),
                    new TypedValue((int)DxfCode.SoftPointerId, ent2.ObjectId),
                    new TypedValue((int)DxfCode.SoftPointerId, ent3.ObjectId)
                    );

                try
                {
                    // Find the NOD in the database
                    DBDictionary nod = (DBDictionary)tr.GetObject(doc.Database.NamedObjectsDictionaryId, OpenMode.ForWrite);
                    // We use Xrecord class to store data in Dictionaries

                    if (nod.Contains(key)) // existing key
                    {
                        xRec = (Xrecord)tr.GetObject(nod.GetAt(key), OpenMode.ForWrite, false);
                        xRec.Data = rsbf;
                    }
                    else // new element
                    {
                        xRec.Data = rsbf;
                        nod.SetAt(key, xRec);
                        tr.AddNewlyCreatedDBObject(xRec, true);
                    }

                }
                catch
                 {
                    doc.Editor.WriteMessage("\nError");
                }

 

 

Read informations :

 

 

 try
                {
                    DBDictionary nod = (DBDictionary)tr.GetObject(doc.Database.NamedObjectsDictionaryId, OpenMode.ForRead);
                    Xrecord readBack = (Xrecord)tr.GetObject(nod.GetAt(key), OpenMode.ForRead);

                    ResultBuffer rs = readBack.Data;

                    if (rs != null)
                    {
                        TypedValue[] result = rs.AsArray();
                        bool isStartItem = true;

                        for (int i = 0; i < result.Length; i++)
                        {
                            if (result[i].TypeCode == (int)DxfCode.SoftPointerId)
                            {
                                if (isStartItem)
                                {
                                    ed.WriteMessage("\nParent element : " + (ObjectId)result[i].Value);
                                    isStartItem = false;
                                }
                                else
                                    ed.WriteMessage("\nChild element : " + (ObjectId)result[i].Value);
                            }
                            else if (result[i].TypeCode == (int)DxfCode.Text)
                                isStartItem = true;
                        }
                    }
                }
                catch
                {
                    doc.Editor.WriteMessage("\nError");
                }

 

 

 

 

 

Message 5 of 28

youssefGC
Advocate
Advocate

Thank you @ActivistInvestor for your response. Actually, in my case, the second TValue element is a list of ObjectIDs, which makes things more complex.

 

0 Likes

Thank you @ActivistInvestor for your response. Actually, in my case, the second TValue element is a list of ObjectIDs, which makes things more complex.

 

Message 6 of 28
youssefGC
in reply to: norman.yuan

youssefGC
Advocate
Advocate

Thank you @norman.yuan for your reply.

Indeed, I don't have a simple list of ObjectIDs, but rather a relationship between one element and several other elements. That's why I'm looking to store this information in the form of a dictionary.

 

0 Likes

Thank you @norman.yuan for your reply.

Indeed, I don't have a simple list of ObjectIDs, but rather a relationship between one element and several other elements. That's why I'm looking to store this information in the form of a dictionary.

 

Message 7 of 28

ActivistInvestor
Advisor
Advisor

@youssefGC wrote:

Thank you @ActivistInvestor for your response. Actually, in my case, the second TValue element is a list of ObjectIDs, which makes things more complex.

 


The methods I posted work for any type of data, not only the types used in the example.

 

The problem is not that, but the way you are going about storing the data and identifying what each element represents. I don't think using delimited or bracketed lists is a wise choice for your use case. I would use multiple Xrecords instead, one for each list of ObjectIds that represent a value in the dictionary.

 

If the Value of each dictionary element is a list of objectids, you could store each as a separate xrecord, along with a 'master' xrecord containing the access keys, and the string key of the xrecord that holds the list of values.

 

This has the advantage of not having to load all of the data to access the elements of a particular key, and not having to write all the data when the elements for a particular key are modified. The DBDictionary keys for each of the xrecords that contain the values are only meaningful to the code that accesses them, so they can be GUID strings.

 

0 Likes


@youssefGC wrote:

Thank you @ActivistInvestor for your response. Actually, in my case, the second TValue element is a list of ObjectIDs, which makes things more complex.

 


The methods I posted work for any type of data, not only the types used in the example.

 

The problem is not that, but the way you are going about storing the data and identifying what each element represents. I don't think using delimited or bracketed lists is a wise choice for your use case. I would use multiple Xrecords instead, one for each list of ObjectIds that represent a value in the dictionary.

 

If the Value of each dictionary element is a list of objectids, you could store each as a separate xrecord, along with a 'master' xrecord containing the access keys, and the string key of the xrecord that holds the list of values.

 

This has the advantage of not having to load all of the data to access the elements of a particular key, and not having to write all the data when the elements for a particular key are modified. The DBDictionary keys for each of the xrecords that contain the values are only meaningful to the code that accesses them, so they can be GUID strings.

 

Message 8 of 28

ActivistInvestor
Advisor
Advisor

What I said above is not entirely true. The code I posted will only store simple values in each dictionary entry's value.  

 

I still believe that the correct way to address your problem is with multiple Xrecords (one representing each entry in a dictionary, with the first element being the 'key', and the remaining elements being the values).

 

However, if you insist on going about it the way you are, you should approach the solution to the problem in a more holistic way, to eliminate the confusion.

 

Here are altered versions of the previously-posted methods that accommodate conversion between  Dictionary<TKey, List<TValue>> and ResultBuffer.

 

public static class Class1
{

   public static ResultBuffer ToResultBuffer<TKey, TValue>(
      this Dictionary<TKey, List<TValue>> items,
      short keyCode,
      short valueCode)
   {
      var result = new ResultBuffer();
      foreach(var pair in items)
      {
         result.Add(new TypedValue(keyCode, pair.Key));
         result.Add(new TypedValue((short)LispDataType.ListBegin));
         foreach(TValue value in pair.Value)
            result.Add(new TypedValue(valueCode, value));
         result.Add(new TypedValue((short)LispDataType.ListEnd));
      }
      return result;
   }

   public static Dictionary<TKey, List<TValue>> ToDictionary<TKey, TValue>(
      this ResultBuffer rb)
   {
      var result = new Dictionary<TKey, List<TValue>>();
      var e = rb.GetEnumerator();
      while(e.MoveNext())
      {
         var key = e.Current.Value;
         if(!e.MoveNext())
            throw new InvalidOperationException("Count mismatch");
         if(e.Current.TypeCode != (short)LispDataType.ListBegin)
            throw new InvalidOperationException("Invalid sequence, expecting ListBegin");
         List<TValue> list = new List<TValue>();
         while(e.MoveNext() && e.Current.TypeCode != (short)LispDataType.ListEnd)
         {
            list.Add((TValue)e.Current.Value);
         }
         if(e.Current.TypeCode != (short)LispDataType.ListEnd)
            throw new InvalidOperationException("Malformed list, expecting ListEnd");
         result.Add((TKey)key, list);
      }
      return result;
   }


   [CommandMethod("TEST")]
   public static void Run()
   {
      /// Create and populate dictionary:

      Dictionary<string, List<int>> map = new Dictionary<string, List<int>>();
      int i = 1;
      int v = 0;
      foreach(string key in new[] { "First", "Second", "Third" })
      {
         List<int> list = new List<int>();
         for(int j = 0; j < (3 * i); j++)
         {
            list.Add(v++);
         }
         map.Add(key, list);
         ++i;
      }
      
      /// Dump contents of dictionary:

      DumpDictionary();

      /// Convert dictionary to resultbuffer:

      ResultBuffer resbuf = map.ToResultBuffer((short) DxfCode.Text, (short) DxfCode.Int32);

      /// Dump contents of resultbuffer:

      foreach(TypedValue tv in resbuf)
         Write($"{(DxfCode)tv.TypeCode} = {tv.Value}");

      /// Convert resultbuffer back to dictionary:
      
      map = resbuf.ToDictionary<string, int>();

      /// Dump contents of dictionary:
      
      DumpDictionary();

      void DumpDictionary()
      {
         foreach(var pair in map)
         {
            Write($"{pair.Key} = "
               + string.Join(", ", 
                     pair.Value.Select(v => v.ToString())));
         }
      }

      void Write(string fmt, params object[] args)
      {
         Application.DocumentManager.MdiActiveDocument?.
            Editor.WriteMessage("\n" + fmt, args);
      }
   }
}

 

Command: TEST
First = 0, 1, 2
Second = 3, 4, 5, 6, 7, 8
Third = 9, 10, 11, 12, 13, 14, 15, 16, 17

Text = First
5016 = -1
Int32 = 0
Int32 = 1
Int32 = 2
5017 = -1
Text = Second
5016 = -1
Int32 = 3
Int32 = 4
Int32 = 5
Int32 = 6
Int32 = 7
Int32 = 8
5017 = -1
Text = Third
5016 = -1
Int32 = 9
Int32 = 10
Int32 = 11
Int32 = 12
Int32 = 13
Int32 = 14
Int32 = 15
Int32 = 16
Int32 = 17
5017 = -1

First = 0, 1, 2
Second = 3, 4, 5, 6, 7, 8
Third = 9, 10, 11, 12, 13, 14, 15, 16, 17

What I said above is not entirely true. The code I posted will only store simple values in each dictionary entry's value.  

 

I still believe that the correct way to address your problem is with multiple Xrecords (one representing each entry in a dictionary, with the first element being the 'key', and the remaining elements being the values).

 

However, if you insist on going about it the way you are, you should approach the solution to the problem in a more holistic way, to eliminate the confusion.

 

Here are altered versions of the previously-posted methods that accommodate conversion between  Dictionary<TKey, List<TValue>> and ResultBuffer.

 

public static class Class1
{

   public static ResultBuffer ToResultBuffer<TKey, TValue>(
      this Dictionary<TKey, List<TValue>> items,
      short keyCode,
      short valueCode)
   {
      var result = new ResultBuffer();
      foreach(var pair in items)
      {
         result.Add(new TypedValue(keyCode, pair.Key));
         result.Add(new TypedValue((short)LispDataType.ListBegin));
         foreach(TValue value in pair.Value)
            result.Add(new TypedValue(valueCode, value));
         result.Add(new TypedValue((short)LispDataType.ListEnd));
      }
      return result;
   }

   public static Dictionary<TKey, List<TValue>> ToDictionary<TKey, TValue>(
      this ResultBuffer rb)
   {
      var result = new Dictionary<TKey, List<TValue>>();
      var e = rb.GetEnumerator();
      while(e.MoveNext())
      {
         var key = e.Current.Value;
         if(!e.MoveNext())
            throw new InvalidOperationException("Count mismatch");
         if(e.Current.TypeCode != (short)LispDataType.ListBegin)
            throw new InvalidOperationException("Invalid sequence, expecting ListBegin");
         List<TValue> list = new List<TValue>();
         while(e.MoveNext() && e.Current.TypeCode != (short)LispDataType.ListEnd)
         {
            list.Add((TValue)e.Current.Value);
         }
         if(e.Current.TypeCode != (short)LispDataType.ListEnd)
            throw new InvalidOperationException("Malformed list, expecting ListEnd");
         result.Add((TKey)key, list);
      }
      return result;
   }


   [CommandMethod("TEST")]
   public static void Run()
   {
      /// Create and populate dictionary:

      Dictionary<string, List<int>> map = new Dictionary<string, List<int>>();
      int i = 1;
      int v = 0;
      foreach(string key in new[] { "First", "Second", "Third" })
      {
         List<int> list = new List<int>();
         for(int j = 0; j < (3 * i); j++)
         {
            list.Add(v++);
         }
         map.Add(key, list);
         ++i;
      }
      
      /// Dump contents of dictionary:

      DumpDictionary();

      /// Convert dictionary to resultbuffer:

      ResultBuffer resbuf = map.ToResultBuffer((short) DxfCode.Text, (short) DxfCode.Int32);

      /// Dump contents of resultbuffer:

      foreach(TypedValue tv in resbuf)
         Write($"{(DxfCode)tv.TypeCode} = {tv.Value}");

      /// Convert resultbuffer back to dictionary:
      
      map = resbuf.ToDictionary<string, int>();

      /// Dump contents of dictionary:
      
      DumpDictionary();

      void DumpDictionary()
      {
         foreach(var pair in map)
         {
            Write($"{pair.Key} = "
               + string.Join(", ", 
                     pair.Value.Select(v => v.ToString())));
         }
      }

      void Write(string fmt, params object[] args)
      {
         Application.DocumentManager.MdiActiveDocument?.
            Editor.WriteMessage("\n" + fmt, args);
      }
   }
}

 

Command: TEST
First = 0, 1, 2
Second = 3, 4, 5, 6, 7, 8
Third = 9, 10, 11, 12, 13, 14, 15, 16, 17

Text = First
5016 = -1
Int32 = 0
Int32 = 1
Int32 = 2
5017 = -1
Text = Second
5016 = -1
Int32 = 3
Int32 = 4
Int32 = 5
Int32 = 6
Int32 = 7
Int32 = 8
5017 = -1
Text = Third
5016 = -1
Int32 = 9
Int32 = 10
Int32 = 11
Int32 = 12
Int32 = 13
Int32 = 14
Int32 = 15
Int32 = 16
Int32 = 17
5017 = -1

First = 0, 1, 2
Second = 3, 4, 5, 6, 7, 8
Third = 9, 10, 11, 12, 13, 14, 15, 16, 17
Message 9 of 28

youssefGC
Advocate
Advocate

Thank you for your code, it seems interesting. I will try to implement it in my case.

 

Yesterday, I tried to develop the following code:

 

 public void StoreData(Document doc, Dictionary<ObjectId, List<ObjectId>> map)
        {
            string Masterkey = "key";

            // Write object id s in NOD 
            Xrecord MasterxRec = new Xrecord();

            using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
            {
                try
                {
                    // Find the NOD in the database
                    DBDictionary nod = (DBDictionary)tr.GetObject(doc.Database.NamedObjectsDictionaryId, OpenMode.ForWrite);

                    // Creates a new DBDictionary
                    if (!nod.Contains(Masterkey))
                    {
                        DBDictionary newDict = new DBDictionary();
                        nod.SetAt(Masterkey, newDict);
                        tr.AddNewlyCreatedDBObject(newDict, true);
                    }

                    MasterxRec = (Xrecord)tr.GetObject(nod.GetAt(Masterkey), OpenMode.ForWrite, false);

                    if (MasterxRec.ExtensionDictionary == null)
                    {
                        MasterxRec.CreateExtensionDictionary();
                    }

                    DBDictionary xDict = tr.GetObject(MasterxRec.ExtensionDictionary, OpenMode.ForWrite) as DBDictionary;
                    foreach (var item in map)
                    {
                        string key = item.Key.Handle.ToString();
                        List<ObjectId> lstChildren = item.Value as List<ObjectId>;
                        ResultBuffer rsb = new ResultBuffer(new TypedValue((int)DxfCode.SoftPointerId, item.Key));
                        Xrecord xr = new Xrecord();

                        foreach (var id in lstChildren)
                        {
                            rsb.Add(new TypedValue((int)DxfCode.SoftPointerId, id));
                        }

                        if (!xDict.Contains(key))
                        {
                            xr.Data = rsb;
                            xDict.SetAt(key, xr);
                            tr.AddNewlyCreatedDBObject(xr, true);
                        }
                        else
                        {
                            xr = (Xrecord)tr.GetObject(nod.GetAt(key), OpenMode.ForWrite, false);
                            xr.Data = rsb;
                        }
                    }
                    
                }
                catch
                {
                    doc.Editor.WriteMessage("\nError");
                }
                tr.Commit();
            }
        }

 

0 Likes

Thank you for your code, it seems interesting. I will try to implement it in my case.

 

Yesterday, I tried to develop the following code:

 

 public void StoreData(Document doc, Dictionary<ObjectId, List<ObjectId>> map)
        {
            string Masterkey = "key";

            // Write object id s in NOD 
            Xrecord MasterxRec = new Xrecord();

            using (Transaction tr = doc.Database.TransactionManager.StartTransaction())
            {
                try
                {
                    // Find the NOD in the database
                    DBDictionary nod = (DBDictionary)tr.GetObject(doc.Database.NamedObjectsDictionaryId, OpenMode.ForWrite);

                    // Creates a new DBDictionary
                    if (!nod.Contains(Masterkey))
                    {
                        DBDictionary newDict = new DBDictionary();
                        nod.SetAt(Masterkey, newDict);
                        tr.AddNewlyCreatedDBObject(newDict, true);
                    }

                    MasterxRec = (Xrecord)tr.GetObject(nod.GetAt(Masterkey), OpenMode.ForWrite, false);

                    if (MasterxRec.ExtensionDictionary == null)
                    {
                        MasterxRec.CreateExtensionDictionary();
                    }

                    DBDictionary xDict = tr.GetObject(MasterxRec.ExtensionDictionary, OpenMode.ForWrite) as DBDictionary;
                    foreach (var item in map)
                    {
                        string key = item.Key.Handle.ToString();
                        List<ObjectId> lstChildren = item.Value as List<ObjectId>;
                        ResultBuffer rsb = new ResultBuffer(new TypedValue((int)DxfCode.SoftPointerId, item.Key));
                        Xrecord xr = new Xrecord();

                        foreach (var id in lstChildren)
                        {
                            rsb.Add(new TypedValue((int)DxfCode.SoftPointerId, id));
                        }

                        if (!xDict.Contains(key))
                        {
                            xr.Data = rsb;
                            xDict.SetAt(key, xr);
                            tr.AddNewlyCreatedDBObject(xr, true);
                        }
                        else
                        {
                            xr = (Xrecord)tr.GetObject(nod.GetAt(key), OpenMode.ForWrite, false);
                            xr.Data = rsb;
                        }
                    }
                    
                }
                catch
                {
                    doc.Editor.WriteMessage("\nError");
                }
                tr.Commit();
            }
        }

 

Message 10 of 28
daniel_cadext
in reply to: youssefGC

daniel_cadext
Advisor
Advisor

There’s another way to do this without the need to use ResultBuffers

SetBinaryDataForKey, GetBinaryDataForKey or setBinaryData, getBinaryData in ARX respectively

 

SetBinaryDataForKey, will automatically create the extension dictionary and Xrecord.

All you need to do is implement is ISerializable on your data.

This is Python, but I hope it illustrates the concept

 

 

import traceback
from pyrx_imp import Ap, Db, Ed, Ge, Gi, Gs, Rx

# pickle == ISerializable 
import pickle

#python dictionary 
data = { "KEY1": 1, "KEY2": 2,  "KEY3": 3}

def PyRxCmd_setit() -> None:
    try:
        esres = Ed.Editor.entSel("\nSelect: ")
        ent = Db.Entity(esres[1], Db.OpenMode.kForWrite)
        ent.setBinaryData("PYTHONTEST", pickle.dumps(data))
    except Exception as err:
        traceback.print_exception(err)
 
def PyRxCmd_getit() -> None:
    try:
        esres = Ed.Editor.entSel("\nSelect: ")
        ent = Db.Entity(esres[1])
        bOut = ent.getBinaryData("PYTHONTEST")
        print(pickle.loads(bOut))
    except Exception as err:
        traceback.print_exception(err)

 

 

x.png

Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx

There’s another way to do this without the need to use ResultBuffers

SetBinaryDataForKey, GetBinaryDataForKey or setBinaryData, getBinaryData in ARX respectively

 

SetBinaryDataForKey, will automatically create the extension dictionary and Xrecord.

All you need to do is implement is ISerializable on your data.

This is Python, but I hope it illustrates the concept

 

 

import traceback
from pyrx_imp import Ap, Db, Ed, Ge, Gi, Gs, Rx

# pickle == ISerializable 
import pickle

#python dictionary 
data = { "KEY1": 1, "KEY2": 2,  "KEY3": 3}

def PyRxCmd_setit() -> None:
    try:
        esres = Ed.Editor.entSel("\nSelect: ")
        ent = Db.Entity(esres[1], Db.OpenMode.kForWrite)
        ent.setBinaryData("PYTHONTEST", pickle.dumps(data))
    except Exception as err:
        traceback.print_exception(err)
 
def PyRxCmd_getit() -> None:
    try:
        esres = Ed.Editor.entSel("\nSelect: ")
        ent = Db.Entity(esres[1])
        bOut = ent.getBinaryData("PYTHONTEST")
        print(pickle.loads(bOut))
    except Exception as err:
        traceback.print_exception(err)

 

 

x.png

Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx
Message 11 of 28
_gile
in reply to: youssefGC

_gile
Mentor
Mentor

Hi,

It looks like there's some confusion with extension dictionaries, 'named' dictionaries and xrecord.
In your code you check if the NOD contains a dictionary named MasterDict and create it if it does not already exists (so far so good).

Then you get an xrecord called MasterRec without checking if MasterDict contains it (which will not be the case if you have just created it). Your code will crash at line 25 if MasterDict does not contais it.

Then you get or create the MasterRec extension dictionary, why? What's the purpose of MasterRec Xrecord if you do not fill it with data? Why not directly fill MasterDict with the xrecords containg the data?



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes

Hi,

It looks like there's some confusion with extension dictionaries, 'named' dictionaries and xrecord.
In your code you check if the NOD contains a dictionary named MasterDict and create it if it does not already exists (so far so good).

Then you get an xrecord called MasterRec without checking if MasterDict contains it (which will not be the case if you have just created it). Your code will crash at line 25 if MasterDict does not contais it.

Then you get or create the MasterRec extension dictionary, why? What's the purpose of MasterRec Xrecord if you do not fill it with data? Why not directly fill MasterDict with the xrecords containg the data?



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 12 of 28

ActivistInvestor
Advisor
Advisor

How would one persist ObjectIds using Set/GetBinaryDataForKey() in a way that allows them to persist across editing sessions and be translated by deep-clone/wblock-clone operations?

 


@daniel_cadext wrote:

There’s another way to do this without the need to use ResultBuffers

SetBinaryDataForKey, GetBinaryDataForKey or setBinaryData, getBinaryData in ARX respectively

 

 

How would one persist ObjectIds using Set/GetBinaryDataForKey() in a way that allows them to persist across editing sessions and be translated by deep-clone/wblock-clone operations?

 


@daniel_cadext wrote:

There’s another way to do this without the need to use ResultBuffers

SetBinaryDataForKey, GetBinaryDataForKey or setBinaryData, getBinaryData in ARX respectively

 

 

Message 13 of 28

daniel_cadext
Advisor
Advisor

Good point! might not be the best tool for this.

Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx
0 Likes

Good point! might not be the best tool for this.

Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx
Message 14 of 28
youssefGC
in reply to: _gile

youssefGC
Advocate
Advocate

Indeed what you say is true: the MasterDic does not contain the MasterRec. Before creating and storing it, I must ensure this.

To my understanding, the storage architecture generally involves creating an "ExtensionDictionary". On this, I create keys to which I attach each XRecord(). XRecords contain information in the form of a series of TypedValues.

I attempted to adapt this architecture to my case: creating a main dictionary "Master" where I want to store XRecords for each main entity "Parent" and the secondary entities "Children" associated with it.

In my case, "MasterxRec" serves as the "Parent", but after your correction, it seems unnecessary. Thank you @_gile again for your feedback.

 

0 Likes

Indeed what you say is true: the MasterDic does not contain the MasterRec. Before creating and storing it, I must ensure this.

To my understanding, the storage architecture generally involves creating an "ExtensionDictionary". On this, I create keys to which I attach each XRecord(). XRecords contain information in the form of a series of TypedValues.

I attempted to adapt this architecture to my case: creating a main dictionary "Master" where I want to store XRecords for each main entity "Parent" and the secondary entities "Children" associated with it.

In my case, "MasterxRec" serves as the "Parent", but after your correction, it seems unnecessary. Thank you @_gile again for your feedback.

 

Message 15 of 28
_gile
in reply to: youssefGC

_gile
Mentor
Mentor

@youssefGC  a écrit :

To my understanding, the storage architecture generally involves creating an "ExtensionDictionary".

 


No, Xrecords can be conained either by enamed dictionaries or by extension dictionaries. Both are the same type: DBDictionary, only the access changes, the first ones are got with their name (key), the second ones by the DBObject they belongs to..



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub


@youssefGC  a écrit :

To my understanding, the storage architecture generally involves creating an "ExtensionDictionary".

 


No, Xrecords can be conained either by enamed dictionaries or by extension dictionaries. Both are the same type: DBDictionary, only the access changes, the first ones are got with their name (key), the second ones by the DBObject they belongs to..



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 16 of 28

youssefGC
Advocate
Advocate

I tried to adapt your code to my case, but I encountered an exception error. I'm unable to add the data from the result buffer to the NOD of the document.

 

 

// Add Data
                Dictionary<ObjectId, List<ObjectId>> map = new Dictionary<ObjectId, List<ObjectId>>();
                List<ObjectId> templst = new List<ObjectId>();
                templst.Add(ent2.ObjectId);
                templst.Add(ent3.ObjectId);
                map.Add(ent1.ObjectId, templst);

                templst = new List<ObjectId>();
                templst.Add(ent5.ObjectId);
                map.Add(ent4.ObjectId, templst);

                // Convert dictionary to resultbuffer
                ResultBuffer resbuf = map.ToResultBuffer((short)DxfCode.SoftPointerId, (short)DxfCode.SoftPointerId);
                ReadAndWriteUsingNOD _instanceClass = new ReadAndWriteUsingNOD();
                _instanceClass.updateNOD(doc, "Test", resbuf);

 

 

Here is the method "updateNOD" which I used to write to the NOD:

 

 

 public void updateNOD(Document doc, string key, ResultBuffer rsbf)
        {
            Database db = doc.Database;

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                Xrecord xRec = new Xrecord();
                try
                {
                    // Find the NOD in the database
                    DBDictionary nod = (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForWrite);
                    // We use Xrecord class to store data in Dictionaries

                    if (nod.Contains(key)) // existing key
                    {
                        xRec = (Xrecord)tr.GetObject(nod.GetAt(key), OpenMode.ForWrite, false);
                        xRec.Data = rsbf;
                    }
                    else // new element
                    {
                        xRec.Data = rsbf;
                        nod.SetAt(key, xRec);
                        tr.AddNewlyCreatedDBObject(xRec, true);
                    }

                    tr.Commit();
                }
                catch
                {
                    doc.Editor.WriteMessage("\nError");
                }
            }

        }

 

 

I found an error on line 21

 

2024-07-25_133403.png

 

 

 

0 Likes

I tried to adapt your code to my case, but I encountered an exception error. I'm unable to add the data from the result buffer to the NOD of the document.

 

 

// Add Data
                Dictionary<ObjectId, List<ObjectId>> map = new Dictionary<ObjectId, List<ObjectId>>();
                List<ObjectId> templst = new List<ObjectId>();
                templst.Add(ent2.ObjectId);
                templst.Add(ent3.ObjectId);
                map.Add(ent1.ObjectId, templst);

                templst = new List<ObjectId>();
                templst.Add(ent5.ObjectId);
                map.Add(ent4.ObjectId, templst);

                // Convert dictionary to resultbuffer
                ResultBuffer resbuf = map.ToResultBuffer((short)DxfCode.SoftPointerId, (short)DxfCode.SoftPointerId);
                ReadAndWriteUsingNOD _instanceClass = new ReadAndWriteUsingNOD();
                _instanceClass.updateNOD(doc, "Test", resbuf);

 

 

Here is the method "updateNOD" which I used to write to the NOD:

 

 

 public void updateNOD(Document doc, string key, ResultBuffer rsbf)
        {
            Database db = doc.Database;

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                Xrecord xRec = new Xrecord();
                try
                {
                    // Find the NOD in the database
                    DBDictionary nod = (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForWrite);
                    // We use Xrecord class to store data in Dictionaries

                    if (nod.Contains(key)) // existing key
                    {
                        xRec = (Xrecord)tr.GetObject(nod.GetAt(key), OpenMode.ForWrite, false);
                        xRec.Data = rsbf;
                    }
                    else // new element
                    {
                        xRec.Data = rsbf;
                        nod.SetAt(key, xRec);
                        tr.AddNewlyCreatedDBObject(xRec, true);
                    }

                    tr.Commit();
                }
                catch
                {
                    doc.Editor.WriteMessage("\nError");
                }
            }

        }

 

 

I found an error on line 21

 

2024-07-25_133403.png

 

 

 

Message 17 of 28

ActivistInvestor
Advisor
Advisor
Accepted solution

Well, I obviously didn't test writing/reading to/from an XRecord, and the problem is that it doesn't like the LispDataType.ListBegin/end codes. That's because it was based on existing code that was designed to pass List and Dictionary objects between LISP and managed code.

 

This corrects the problem, and was round-trip tested:

 

public static class ResultBufferUtils
{

   /// ResultBuffer conversion to/from Dictionary<TKey, List<TValue>>:

   public static ResultBuffer ToResultBuffer<TKey, TValue>(
      this Dictionary<TKey, IEnumerable<TValue>> items,
      DxfCode keyCode,
      DxfCode valueCode)
   {
      return ToResultBuffer<TKey, TValue>(items, (short) keyCode, (short) valueCode);
   }

   public static ResultBuffer ToResultBuffer<TKey, TValue>(
      this Dictionary<TKey, IEnumerable<TValue>> items,
      short keyCode,
      short valueCode)
   {
      if(items == null)
         throw new ArgumentNullException(nameof(items));
      var result = new ResultBuffer();
      foreach(var pair in items)
      {
         result.Add(new TypedValue(keyCode, pair.Key));
         result.Add(listBegin);
         if(pair.Value != null)
         {
            foreach(TValue value in pair.Value)
               result.Add(new TypedValue(valueCode, value));
         }
         result.Add(listEnd);
      }
      return result;
   }

   /// Note: In AcMgdLib, the name of this method was
   /// changed to 'ToListDictionary()', to accomodate both
   /// dictionaries with simple values and dictionaries
   /// with List<T> values.
   public static Dictionary<TKey, IEnumerable<TValue>> 
   ToDictionary<TKey, TValue>(this ResultBuffer resbuf)
   {
      if(resbuf == null)
         throw new ArgumentNullException(nameof(resbuf));
      var result = new Dictionary<TKey, IEnumerable<TValue>>();
      var e = resbuf.GetEnumerator();
      while(e.MoveNext())
      {
         var key = e.Current.Value;
         if(!e.MoveNext())
            throw new InvalidOperationException("Count mismatch");
         TypedValue cur = e.Current;
         if(!IsListBegin(cur))
            throw new InvalidOperationException(
               $"Malformed list: expecting List Begin: {cur.TypeCode}, {cur.Value}");
         ICollection<TValue> list = new List<TValue>();
         while(true)
         {
            if(!e.MoveNext())
               throw new InvalidOperationException(
                  $"Malformed list: expecting List End: {cur.TypeCode}, {cur.Value}");
            cur = e.Current;
            if(IsListEnd(cur))
               break;
            list.Add((TValue) cur.Value);
         }
         result.Add((TKey)key, list);
      }
      return result;
   }

   static readonly TypedValue listBegin = new TypedValue(102, "{");
   static readonly TypedValue listEnd = new TypedValue(102, "}");

   // Note: Need to avoid using TypedValue.Equals() because of a bug in that API.
   
   static bool IsListBegin(TypedValue tv)
      => tv.TypeCode == 102 && object.Equals(tv.Value, "{");
   static bool IsListEnd(TypedValue tv) 
      => tv.TypeCode == 102 && object.Equals(tv.Value, "}");
}

 

I also added these APIs to this library, along with a few example commands that were used to test it.

 

Well, I obviously didn't test writing/reading to/from an XRecord, and the problem is that it doesn't like the LispDataType.ListBegin/end codes. That's because it was based on existing code that was designed to pass List and Dictionary objects between LISP and managed code.

 

This corrects the problem, and was round-trip tested:

 

public static class ResultBufferUtils
{

   /// ResultBuffer conversion to/from Dictionary<TKey, List<TValue>>:

   public static ResultBuffer ToResultBuffer<TKey, TValue>(
      this Dictionary<TKey, IEnumerable<TValue>> items,
      DxfCode keyCode,
      DxfCode valueCode)
   {
      return ToResultBuffer<TKey, TValue>(items, (short) keyCode, (short) valueCode);
   }

   public static ResultBuffer ToResultBuffer<TKey, TValue>(
      this Dictionary<TKey, IEnumerable<TValue>> items,
      short keyCode,
      short valueCode)
   {
      if(items == null)
         throw new ArgumentNullException(nameof(items));
      var result = new ResultBuffer();
      foreach(var pair in items)
      {
         result.Add(new TypedValue(keyCode, pair.Key));
         result.Add(listBegin);
         if(pair.Value != null)
         {
            foreach(TValue value in pair.Value)
               result.Add(new TypedValue(valueCode, value));
         }
         result.Add(listEnd);
      }
      return result;
   }

   /// Note: In AcMgdLib, the name of this method was
   /// changed to 'ToListDictionary()', to accomodate both
   /// dictionaries with simple values and dictionaries
   /// with List<T> values.
   public static Dictionary<TKey, IEnumerable<TValue>> 
   ToDictionary<TKey, TValue>(this ResultBuffer resbuf)
   {
      if(resbuf == null)
         throw new ArgumentNullException(nameof(resbuf));
      var result = new Dictionary<TKey, IEnumerable<TValue>>();
      var e = resbuf.GetEnumerator();
      while(e.MoveNext())
      {
         var key = e.Current.Value;
         if(!e.MoveNext())
            throw new InvalidOperationException("Count mismatch");
         TypedValue cur = e.Current;
         if(!IsListBegin(cur))
            throw new InvalidOperationException(
               $"Malformed list: expecting List Begin: {cur.TypeCode}, {cur.Value}");
         ICollection<TValue> list = new List<TValue>();
         while(true)
         {
            if(!e.MoveNext())
               throw new InvalidOperationException(
                  $"Malformed list: expecting List End: {cur.TypeCode}, {cur.Value}");
            cur = e.Current;
            if(IsListEnd(cur))
               break;
            list.Add((TValue) cur.Value);
         }
         result.Add((TKey)key, list);
      }
      return result;
   }

   static readonly TypedValue listBegin = new TypedValue(102, "{");
   static readonly TypedValue listEnd = new TypedValue(102, "}");

   // Note: Need to avoid using TypedValue.Equals() because of a bug in that API.
   
   static bool IsListBegin(TypedValue tv)
      => tv.TypeCode == 102 && object.Equals(tv.Value, "{");
   static bool IsListEnd(TypedValue tv) 
      => tv.TypeCode == 102 && object.Equals(tv.Value, "}");
}

 

I also added these APIs to this library, along with a few example commands that were used to test it.

 

Message 18 of 28
norman.yuan
in reply to: youssefGC

norman.yuan
Mentor
Mentor

As the rule of thumb regarding to using NamedDictionary of the drawing database, you DO NOT add your data (Xrecords) as DIRECT CHILDREN of the database's NamedDictionary. The direct children of the database's dictionary should always be DBDictionaries, which are specific for different AutoCAD vertical products/plugins. So, you need to first add your own DBDictionary to the NamedDictionary of the database, then add your data as Xrecord, or add another level of DBDictionary...

 

Norman Yuan

Drive CAD With Code

EESignature

As the rule of thumb regarding to using NamedDictionary of the drawing database, you DO NOT add your data (Xrecords) as DIRECT CHILDREN of the database's NamedDictionary. The direct children of the database's dictionary should always be DBDictionaries, which are specific for different AutoCAD vertical products/plugins. So, you need to first add your own DBDictionary to the NamedDictionary of the database, then add your data as Xrecord, or add another level of DBDictionary...

 

Norman Yuan

Drive CAD With Code

EESignature

Message 19 of 28

ActivistInvestor
Advisor
Advisor

There isn't any problem using an Xrecord as a direct child of the NamedObjectsDictionary, so long as you use a Key that is reasonably-distinct and would not likely be used by another app or AutoCAD. Having to use a child DBDictionary is a pita, and doesn't serve any useful purpose other than to complicate code that must access the Xrecord. Using a child DBDictionary is something you should do if you have multiple, related dictionary entries that need to be stored in the NOD (e.g., you may need to deep clone the parent dictionary and all of its contents to another database, which is easier when they are all contained in a single parent dictionary). However, in the case where you only need to store a single Xrecord, and you don't expect that to change, there is no problem with storing it in the root dictionary.

 

The fact is, that any two applications could potentially use dictionary keys that collide, regardless of whether they are keys for a child DBDictionary or an Xrecord.

 

The only rule of thumb is to avoid the use of any key that starts with "ACAD".

There isn't any problem using an Xrecord as a direct child of the NamedObjectsDictionary, so long as you use a Key that is reasonably-distinct and would not likely be used by another app or AutoCAD. Having to use a child DBDictionary is a pita, and doesn't serve any useful purpose other than to complicate code that must access the Xrecord. Using a child DBDictionary is something you should do if you have multiple, related dictionary entries that need to be stored in the NOD (e.g., you may need to deep clone the parent dictionary and all of its contents to another database, which is easier when they are all contained in a single parent dictionary). However, in the case where you only need to store a single Xrecord, and you don't expect that to change, there is no problem with storing it in the root dictionary.

 

The fact is, that any two applications could potentially use dictionary keys that collide, regardless of whether they are keys for a child DBDictionary or an Xrecord.

 

The only rule of thumb is to avoid the use of any key that starts with "ACAD".

Message 20 of 28
Izhar_Azati
in reply to: youssefGC

Izhar_Azati
Enthusiast
Enthusiast

If the number of "children" is not large, and the logic is saved in the file, it is advisable to also check XDATA with a list of DxfCode.ExtendedDataHandle
It's true that it came out a bad name for XDATA but because of incorrect use of the RegAppTableRecord registration

 

			var values = new _AcDb.TypedValue[12 + InnerPolygonsHandles.Count];
			values[0] = new _AcDb.TypedValue( /*1001*/ (short)_AcDb.DxfCode.ExtendedDataRegAppName, MyXData.MidotPolygonAppId );
			values[1] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, LocalityId );
			values[2] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, MapId );
			values[3] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, DrawingId );
			values[4] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, PolyId );
			values[5] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, BuildingId );
			values[6] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, PolyNum );
			values[7] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, Floor );
			values[8] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, MainKind );
			values[9] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, SubKind );
			values[10] = new _AcDb.TypedValue( /*1005*/ (short)_AcDb.DxfCode.ExtendedDataHandle, AttributesBlockHandle );
			values[11] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, InnerPolygonsHandles.Count );
			for( int i = 0; i < InnerPolygonsHandles.Count; i++ ) {
				values[12 + i] = new _AcDb.TypedValue( /*1005*/ (short)_AcDb.DxfCode.ExtendedDataHandle, InnerPolygonsHandles[i] );
			}
0 Likes

If the number of "children" is not large, and the logic is saved in the file, it is advisable to also check XDATA with a list of DxfCode.ExtendedDataHandle
It's true that it came out a bad name for XDATA but because of incorrect use of the RegAppTableRecord registration

 

			var values = new _AcDb.TypedValue[12 + InnerPolygonsHandles.Count];
			values[0] = new _AcDb.TypedValue( /*1001*/ (short)_AcDb.DxfCode.ExtendedDataRegAppName, MyXData.MidotPolygonAppId );
			values[1] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, LocalityId );
			values[2] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, MapId );
			values[3] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, DrawingId );
			values[4] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, PolyId );
			values[5] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, BuildingId );
			values[6] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, PolyNum );
			values[7] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, Floor );
			values[8] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, MainKind );
			values[9] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, SubKind );
			values[10] = new _AcDb.TypedValue( /*1005*/ (short)_AcDb.DxfCode.ExtendedDataHandle, AttributesBlockHandle );
			values[11] = new _AcDb.TypedValue( /*1071*/ (short)_AcDb.DxfCode.ExtendedDataInteger32, InnerPolygonsHandles.Count );
			for( int i = 0; i < InnerPolygonsHandles.Count; i++ ) {
				values[12 + i] = new _AcDb.TypedValue( /*1005*/ (short)_AcDb.DxfCode.ExtendedDataHandle, InnerPolygonsHandles[i] );
			}

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

AutoCAD Inside the Factory


Autodesk Design & Make Report