Author Topic: How can I to get the list of all types which objects are available in a drawing  (Read 9036 times)

0 Members and 1 Guest are viewing this topic.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Hi all. Sorry for my bad English.
The dictionary of the identifiers grouped by types is necessary to me. If in a cycle each time I will check dictionary key existence (look a code below), then the code will be slower. I want to receive at first all keys, to add them in the dictionary, and only after that to take identifiers and to group them by dictionary keys. Look my commented code row:
Code: [Select]
...
Dictionary<string, List<ObjectId>> dict = new Dictionary<string, List<ObjectId>>();
using (Transaction t = Database.TransactionManager.StartTransaction()) {
for (long i = Database.BlockTableId.Handle.Value; i < Database.Handseed.Value; i++) {
ObjectId id = ObjectId.Null;
Handle h = new Handle(i);
if (IsValidHandle(h, ref id) && filter(t, id)) {
string type = id.ObjectClass.Name;
if (!dict.Keys.Contains(type))// <= My question about it code row - it isn't pleasant to me...
dict.Add(type, new List<ObjectId>());
dict[type].Add(id);
}
}
t.Abort();
}
...
How can I to get the list of all types which objects are available in a drawing database?

MexicanCustard

  • Swamp Rat
  • Posts: 705
This is how I would do what you're showing without your method calls in the if statement.

Code: [Select]
        public static void Test()
        {
            Database db = MgdAcApplication.DocumentManager.MdiActiveDocument.Database;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                var dict = new Dictionary<string, List<ObjectId>>();
                var btr = (BlockTableRecord) db.BlockTableId.GetObject(OpenMode.ForRead);
                foreach (ObjectId id in btr)
                {
                    if (!dict.ContainsKey(id.ObjectClass.Name))
                        dict.Add(id.ObjectClass.Name, new List<ObjectId>());
                    dict[id.ObjectClass.Name].Add(id);
                }
                tr.Commit();////Not neccessary
            }
        }

You realize that that your code is only generating a list of objects in the BlockTableRecord which are mostly going to be more BlockTableRecords.
« Last Edit: June 18, 2012, 12:51:25 PM by MexicanCustard »
Revit 2019, AMEP 2019 64bit Win 10

kaefer

  • Guest
group them by dictionary keys

Sounds like IEnumerable.GroupBy<> could be for you.

Code - C#: [Select]
  1.         [CommandMethod("GroupObjectClassTest")]
  2.         static public void groupObjectClassTestCmd()
  3.         {
  4.             Database db = acadApp.DocumentManager.MdiActiveDocument.Database;
  5.             Editor ed = acadApp.DocumentManager.MdiActiveDocument.Editor;
  6.  
  7.             ObjectId outId = ObjectId.Null;
  8.             Dictionary<RXClass,List<ObjectId>> res =
  9.                 generateHandles(db)
  10.                 .Select(hnd => new { ok = db.TryGetObjectId(hnd, out outId), id = outId })
  11.                 .Where(x => x.ok)
  12.                 .Select(x => x.id)
  13.                 .GroupBy(id => id.ObjectClass)
  14.                 .ToDictionary(grp => grp.Key, grp => grp.ToList());
  15.  
  16.             foreach (KeyValuePair<RXClass,List<ObjectId>> kvp in res)
  17.                 ed.WriteMessage("\n{0}: {1} ", kvp.Key.Name, kvp.Value.Count);
  18.         }
  19.         static private IEnumerable<Handle> generateHandles(Database db)
  20.         {
  21.             for (Int64 i = db.BlockTableId.Handle.Value; i < db.Handseed.Value; i++)
  22.                 yield return new Handle(i);
  23.         }

Code - F#: [Select]
  1. [<CommandMethod "GroupObjectClassTest">]
  2. let groupObjectClassTestCmd() =
  3.     let db = acadApp.DocumentManager.MdiActiveDocument.Database
  4.     let ed = acadApp.DocumentManager.MdiActiveDocument.Editor
  5.  
  6.     let res =
  7.         { db.BlockTableId.Handle.Value .. db.Handseed.Value - 1L }
  8.         |> Seq.map (fun i -> new Handle(i) |> db.TryGetObjectId)
  9.         |> Seq.filter fst
  10.         |> Seq.map snd
  11.         |> Seq.groupBy (fun oid -> oid.ObjectClass)
  12.         |> dict
  13.  
  14.     for KeyValue(rx, oids) in res do
  15.         ed.WriteMessage("\n{0}: {1} ", rx.Name, Seq.length oids)


Se7en's EDIT: I apologize, but I am removing the geshi code tags from this message to check on an error in the forums database. ...I put the geshi code tags back.
« Last Edit: June 21, 2012, 11:08:24 AM by Se7en »

TheMaster

  • Guest
How can I to get the list of all types which objects are available in a drawing database?

Before we reach into the MgdDbg grab-bag, could you clarify what you mean by 'objects' ?

Do you mean all database objects, or only entities?  If the latter, do you mean all entities
in all blocks, or only those in layout blocks, or only those in modelspace, etc.

I'm asking this because I can't see a need for that kind of operation for any type of
database object and every single object in a database, unless you are writing some
kind of database viewer or something.


Andrey Bushman

  • Swamp Rat
  • Posts: 864
I thank all for answers. I will compare speeds of performance of a code.

I'm asking this because I can't see a need for that kind of operation for any type of
database object and every single object in a database, unless you are writing some
kind of database viewer or something.
It is very strange, since there is a lot of such tasks...
For example:
it is necessary to remove text style. But this style can be used by different primitives (the text, the sizes, attributes, etc.) therefore at first it is necessary to find all these primitives in a database and to appoint by it other text style. These primitives can be anywhere. The similar task can arise and for other styles: dimensional styles, table styles, etc.

Do you mean all database objects, or only entities?
DBObjects only.
If the latter, do you mean all entities in all blocks, or only those in layout blocks, or only those in modelspace, etc.
Above I specified an example. I hope, this example gives the clear answer to your question: it is necessary to look for everywhere since the necessary objects can be anywhere. If not to find at least one, then it will not be possible to remove text style.
« Last Edit: June 19, 2012, 02:08:13 AM by Andrey »

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Hi,

Quote
it is necessary to remove text style. But this style can be used by different primitives (the text, the sizes, attributes, etc.) therefore at first it is necessary to find all these primitives in a database and to appoint by it other text style. These primitives can be anywhere. The similar task can arise and for other styles: dimensional styles, table styles, etc.

What about the Database.Purge() method ?
Speaking English as a French Frog

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Hi,

Quote
it is necessary to remove text style. But this style can be used by different primitives (the text, the sizes, attributes, etc.) therefore at first it is necessary to find all these primitives in a database and to appoint by it other text style. These primitives can be anywhere. The similar task can arise and for other styles: dimensional styles, table styles, etc.

What about the Database.Purge() method ?
What relation has this method to the specified task?

Andrey Bushman

  • Swamp Rat
  • Posts: 864
2 kaefer
I wrote the test code measuring productivity:
Code: [Select]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dgs = System.Diagnostics;
using AcApp = Autodesk.AutoCAD.ApplicationServices;
using acad = Autodesk.AutoCAD.ApplicationServices.Application;
using AcDb = Autodesk.AutoCAD.DatabaseServices;
using AcEd = Autodesk.AutoCAD.EditorInput;
using AcGem = Autodesk.AutoCAD.Geometry;
using AcCol = Autodesk.AutoCAD.Colors;
using AcRtm = Autodesk.AutoCAD.Runtime;

[assembly:AcRtm.CommandClass(typeof(SpeedTest.Testing))]

namespace SpeedTest {

public class Testing {

//to fill the Database with data...
[AcRtm.CommandMethod("FILLDATA")]
public void FillDatabase() {
AcApp.Document doc = acad.DocumentManager.MdiActiveDocument;
AcDb.Database db = doc.Database;
AcDb.HostApplicationServices.WorkingDatabase = db;
AcEd.Editor ed = doc.Editor;
ed.WriteMessage("\nInitialize started...");
using (AcDb.Transaction t = db.TransactionManager.StartTransaction()) {
Random rnd = new Random();
AcDb.BlockTable bt = t.GetObject(db.BlockTableId, AcDb.OpenMode.ForWrite) as AcDb.BlockTable;
AcDb.BlockTableRecord btr = t.GetObject(bt[AcDb.BlockTableRecord.ModelSpace], AcDb.OpenMode.ForWrite) as AcDb.BlockTableRecord;
DateTime startTime = DateTime.Now;
for (int i = 0; i < 2000000; i++) {
AcDb.Circle circle = new AcDb.Circle();
circle.SetDatabaseDefaults();
Double x = rnd.Next(0, 1000000);
Double y = rnd.Next(0, 1000000);
Double radius = rnd.Next(10, 1000);
circle.Center = new AcGem.Point3d(x, y, 0);
circle.Radius = radius;
Byte r = (Byte) rnd.Next(0, Byte.MaxValue);
Byte g = (Byte)rnd.Next(0, Byte.MaxValue);
Byte b = (Byte)rnd.Next(0, Byte.MaxValue);
AcCol.Color color = AcCol.Color.FromRgb(r,g,b);
circle.Color = color;
btr.AppendEntity(circle);
t.AddNewlyCreatedDBObject(circle, true);
}
t.Commit();
DateTime finishTime = DateTime.Now;
ed.WriteMessage("\nInitialize finished.\nInitialization time: {0}", finishTime - startTime);
}
}

[AcRtm.CommandMethod("var1")]
public void Variant1() {
AcApp.Document doc = acad.DocumentManager.MdiActiveDocument;
AcDb.Database db = doc.Database;
AcDb.HostApplicationServices.WorkingDatabase = db;
AcEd.Editor ed = doc.Editor;
Dictionary<string, List<AcDb.ObjectId>> dict = GetGroupedObjectIds((t, id) => true);
foreach (KeyValuePair<String, List<AcDb.ObjectId>> kvp in dict)
ed.WriteMessage("\n{0}: {1} ", kvp.Key, kvp.Value.Count);
}

public Dictionary<string, List<AcDb.ObjectId>> GetGroupedObjectIds(Func<AcDb.Transaction, AcDb.ObjectId, bool> filter) {
AcApp.Document doc = acad.DocumentManager.MdiActiveDocument;
AcDb.Database db = doc.Database;
AcDb.HostApplicationServices.WorkingDatabase = db;
AcEd.Editor ed = doc.Editor;
//Dgs.Debug.Assert(null != filter, "null != filter");
//if (null == filter)
//    throw new ArgumentNullException("Argument is null");

Dictionary<string, List<AcDb.ObjectId>> dict = new Dictionary<string, List<AcDb.ObjectId>>();
using (AcDb.Transaction t = db.TransactionManager.StartTransaction()) {
DateTime startTime = DateTime.Now;
ed.WriteMessage("\nVariant №1 started...");
for (long i = db.BlockTableId.Handle.Value; i < db.Handseed.Value; i++) {
AcDb.ObjectId id = AcDb.ObjectId.Null;
AcDb.Handle h = new AcDb.Handle(i);
if (db.TryGetObjectId(h, out id)  && !id.IsNull && id.IsValid && filter(t, id)) {
string type = id.ObjectClass.Name;
if (!dict.Keys.Contains(type))
dict.Add(type, new List<AcDb.ObjectId>());
dict[type].Add(id);
}
}
t.Abort();
DateTime finishTime = DateTime.Now;
ed.WriteMessage("\nVariant №1 finished.\nOperation time: {0}", finishTime - startTime);
}
return dict;
}
[AcRtm.CommandMethod("var2")]
public void Variant2() {
AcApp.Document doc = acad.DocumentManager.MdiActiveDocument;
AcDb.Database db = doc.Database;
AcDb.HostApplicationServices.WorkingDatabase = db;
AcEd.Editor ed = doc.Editor;

AcDb.ObjectId outId = AcDb.ObjectId.Null;
DateTime startTime = DateTime.Now;
ed.WriteMessage("\nVariant №2 started...");
Dictionary<AcRtm.RXClass, List<AcDb.ObjectId>> res =
generateHandles(db)
.Select(hnd => new {
ok = db.TryGetObjectId(hnd, out outId),
id = outId
})
.Where(x => x.ok)
.Select(x => x.id)
.GroupBy(id => id.ObjectClass)
.ToDictionary(grp => grp.Key, grp => grp.ToList());

DateTime finishTime = DateTime.Now;
ed.WriteMessage("\nVariant №2 finished.\nOperation time: {0}", finishTime - startTime);
foreach (KeyValuePair<AcRtm.RXClass, List<AcDb.ObjectId>> kvp in res)
ed.WriteMessage("\n{0}: {1} ", kvp.Key.Name, kvp.Value.Count);
}

static private IEnumerable<AcDb.Handle> generateHandles(AcDb.Database db) {
for (Int64 i = db.BlockTableId.Handle.Value; i < db.Handseed.Value; i++)
yield return new AcDb.Handle(i);
}
}
}
Result:
Quote
Command: netload
Command: var1

Variant №1 started...
Variant №1 finished.
Operation time: 00:00:02.6561650
AcDbBlockTable: 1
AcDbLayerTable: 1
AcDbTextStyleTable: 1
AcDbLinetypeTable: 1
AcDbViewTable: 1
AcDbUCSTable: 1
AcDbViewportTable: 1
AcDbRegAppTable: 1
AcDbDimStyleTable: 1
AcDbDictionary: 16
AcDbDictionaryWithDefault: 1
AcDbPlaceHolder: 1
AcDbLayerTableRecord: 1
AcDbTextStyleTableRecord: 2
AcDbRegAppTableRecord: 7
AcDbLinetypeTableRecord: 3
AcDbMlineStyle: 1
AcDbBlockTableRecord: 3
AcDbBlockBegin: 3
AcDbBlockEnd: 3
AcDbLayout: 3
AcDbDimStyleTableRecord: 3
AcDbDictionaryVar: 7
AcDbTableStyle: 1
AcDbViewportTableRecord: 1
AcDbMaterial: 3
AcDbVisualStyle: 16
AcDbScale: 17
AcDbMLeaderStyle: 2
AcDbXrecord: 5
AcDbCircle: 2000000
AcDbCellStyleMap: 1
AcDbFontTable: 1
AcDbVXTable: 1
AcDbFontTableRecord: 2
Command: var2

Variant №2 started...
Variant №2 finished.
Operation time: 00:00:02.5936670
AcDbBlockTable: 1
AcDbLayerTable: 1
AcDbTextStyleTable: 1
AcDbLinetypeTable: 1
AcDbViewTable: 1
AcDbUCSTable: 1
AcDbViewportTable: 1
AcDbRegAppTable: 1
AcDbDimStyleTable: 1
AcDbDictionary: 16
AcDbDictionaryWithDefault: 1
AcDbPlaceHolder: 1
AcDbLayerTableRecord: 1
AcDbTextStyleTableRecord: 2
AcDbRegAppTableRecord: 7
AcDbLinetypeTableRecord: 3
AcDbMlineStyle: 1
AcDbBlockTableRecord: 3
AcDbBlockBegin: 3
AcDbBlockEnd: 3
AcDbLayout: 3
AcDbDimStyleTableRecord: 3
AcDbDictionaryVar: 7
AcDbTableStyle: 1
AcDbViewportTableRecord: 1
AcDbMaterial: 3
AcDbVisualStyle: 16
AcDbScale: 17
AcDbMLeaderStyle: 2
AcDbXrecord: 5
AcDbCircle: 2000000
AcDbCellStyleMap: 1
AcDbFontTable: 1
AcDbVXTable: 1
AcDbFontTableRecord: 2
Your code works quicker.
« Last Edit: June 19, 2012, 05:11:08 AM by Andrey »

Andrey Bushman

  • Swamp Rat
  • Posts: 864
2 kaefer
But if I change your code so (I think, that these checks need to be added in a code):
Code: [Select]
ok = db.TryGetObjectId(hnd, out outId) && !outId.IsNull && outId.IsValid,
and
Code: [Select]
KeyValuePair<String, List<AcDb.ObjectId>I change key type.

Then my code works quickly than your:
Quote
Variant №1 started...
Variant №1 finished.
Operation time: 00:00:02.8905325
...
Variant №2 started...
Variant №2 finished.
Operation time: 00:00:03.1405245

****************************************

ariant №1 started...
Variant №1 finished.
Operation time: 00:00:02.8280345
....
Variant №2 started...
Variant №2 finished.
Operation time: 00:00:02.9217815

****************************************

Variant №1 started...
Variant №1 finished.
Operation time: 00:00:02.5780425
...
Variant №2 started...
Variant №2 finished.
Operation time: 00:00:02.9061570
« Last Edit: June 19, 2012, 05:15:29 AM by Andrey »

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Hi,

Quote
it is necessary to remove text style. But this style can be used by different primitives (the text, the sizes, attributes, etc.) therefore at first it is necessary to find all these primitives in a database and to appoint by it other text style. These primitives can be anywhere. The similar task can arise and for other styles: dimensional styles, table styles, etc.

What about the Database.Purge() method ?
What relation has this method to the specified task?
I'm with gile on this. The Database.Purge() method, does not purge the styles / blocks / etc. It simply filters out those which cannot be purged. As a sample of its use, see Kean Walmsley's example code for "purging" unused Annotation Scales: http://through-the-interface.typepad.com/through_the_interface/2008/05/deleting-unused.html
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
I'm with gile on this.
I need to remove styles which break our corporate standard. But these styles are used by primitives. Thus the Purge method won't help with this situation.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
OK, so you want to get thew entities / objects which refer to the styles you want out of there. Sorry, from your post I must've misunderstood.

I.e. you want to change them to a "standard" style according to your settings? Now I understand! Sorry, it does seem that you're on the right track then - unless someone knows of a way to get at the internals of that Purge method. I'm guessing it does much the same thing internally as you are trying now: i.e. stepping through the entire database to see if there's something which is referring to the set of styles.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

owenwengerd

  • Bull Frog
  • Posts: 451
There is no robust way using the .NET API to find all referencing objects. You can look for references from known object types, but even known object types can have references in unusual ways (attached reactor, xdata, unanticipated new references in a new version of the object). The only robust way to get all interobject references is to file every object to a DXF or DWG filer, which is only possible using the ObjectARX API.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
There is no robust way using the .NET API to find all referencing objects. You can look for references from known object types, but even known object types can have references in unusual ways (attached reactor, xdata, unanticipated new references in a new version of the object). The only robust way to get all interobject references is to file every object to a DXF or DWG filer, which is only possible using the ObjectARX API.
It is interesting. Can you write an example a code which are showing the problem about what you tell?
Regards

TheMaster

  • Guest
There is no robust way using the .NET API to find all referencing objects.

There certainly is a robust way to do that with .NET.

Quote
You can look for references from known object types, but even known object types can have references in unusual ways (attached reactor, xdata, unanticipated new references in a new version of the object). The only robust way to get all interobject references is to file every object to a DXF or DWG filer, which is only possible using the ObjectARX API.

Code - C#: [Select]
  1.  
  2. public class RefSearchFiler : Autodesk.AutoCAD.DatabaseServices.DwgFiler
  3. {
  4.   private ObjectId referencedId;
  5.   private DBObject current = null;
  6.  
  7.   private ObjectIdCollection referencingObjectIds = new ObjectIdCollection();
  8.  
  9.   // Pass the Id of the object to find references to:
  10.  
  11.   public RefSearchFiler( ObjectId referencedId )
  12.   {
  13.     this.referencedId = referencedId;
  14.   }
  15.  
  16.   // Search the parameter for references to the referencedId:
  17.  
  18.   public void Find( DBObject obj )
  19.   {
  20.     this.current = obj;
  21.     try
  22.     {
  23.       obj.DwgOut( this );
  24.     }
  25.     finally
  26.     {
  27.       this.current = null;
  28.     }
  29.   }
  30.  
  31.   // The ObjectIds of all referencing objects:
  32.  
  33.   public ObjectIdCollection ReferencingObjectIds
  34.   {
  35.     get
  36.     {
  37.       return referencingObjectIds;
  38.     }
  39.   }
  40.  
  41.   public override void WriteSoftOwnershipId( ObjectId value )
  42.   {
  43.     OnWriteReference( value );
  44.   }
  45.  
  46.   public override void WriteSoftPointerId( ObjectId value )
  47.   {
  48.     OnWriteReference( value );
  49.   }
  50.  
  51.   public override void WriteHardOwnershipId( ObjectId value )
  52.   {
  53.     OnWriteReference( value );
  54.   }
  55.  
  56.   public override void WriteHardPointerId( ObjectId value )
  57.   {
  58.     OnWriteReference( value );
  59.   }
  60.  
  61.   // In this case, we don't care about whether a reference
  62.   // is hard/soft/owner/pointer, but we could discriminate
  63.   // further if needed.
  64.  
  65.   void OnWriteReference( ObjectId value )
  66.   {
  67.     if( value == referencedId && this.current != null )
  68.     {
  69.       referencingObjectIds.Add( this.current.ObjectId );
  70.       this.current = null;
  71.     }
  72.   }
  73. }
  74.  
  75.  

   :laugh:



Se7en's EDIT: I apologize, but I am removing the geshi code tags from this message to check on an error in the forums database. ...I put the geshi code tags back.
« Last Edit: June 21, 2012, 11:09:05 AM by Se7en »

owenwengerd

  • Bull Frog
  • Posts: 451
I stand corrected. I didn't know they exposed the DWG filer in the .NET API.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
2 TheMaster
Thank you very much. I try to understand your code. Earlier I never worked with the class DwgFiler.

Your code isn't compiled, since it is necessary to realize 44 more abstract members. :(
« Last Edit: June 20, 2012, 03:18:18 AM by Andrey »

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Code: [Select]
// Pass the Id of the object to find references to:
  public RefSearchFiler( ObjectId referencedId )  {
    this.referencedId = referencedId;
  }
  // Search the parameter for references to the referencedId:
  public void Find( DBObject obj )
  {
    this.current = obj;
    try
    {
      obj.DwgOut( this );
    }
    finally
    {
      this.current = null;
    }
  }
Probably I incorrectly translate comments into Russian...
Quote
Pass the Id of the object to find references to:
If I want to find all objects which use "MyTextStyle"text style, then should I pass the identifier (ObjectId) of this text style in parameter?

Quote
// Search the parameter for references to the referencedId:
Should  I specify object of my text style (TextStyleTableRecord) as parameter?

Regards
« Last Edit: June 20, 2012, 06:08:21 AM by Andrey »

TheMaster

  • Guest
I thank all for answers. I will compare speeds of performance of a code.

It is very strange, since there is a lot of such tasks...
For example:
it is necessary to remove text style. But this style can be used by different primitives (the text, the sizes, attributes, etc.) therefore at first it is necessary to find all these primitives in a database and to appoint by it other text style. These primitives can be anywhere. The similar task can arise and for other styles: dimensional styles, table styles, etc.


The process of grouping all objects in a drawing by their runtime class takes as long
as simply searching all objects for references directly, and as soon as the user gets
control again, you can't rely on the cached results of the grouping because they are
no longer valid.

If you are trying to find references to multiple objects, that can be done as well with
a single pass over the database.

Quote
Above I specified an example. I hope, this example gives the clear answer to your question: it is necessary to look for everywhere since the necessary objects can be anywhere. If not to find at least one, then it will not be possible to remove text style.

Sorry, no. I have written quite a bit of code that finds references to a single object
as well as multiple objects, and there has never been a need to group objects by
their type or runtime class, and doing that would not make the process any faster.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
... and there has never been a need to group objects by
their type or runtime class, and doing that would not make the process any faster.
Sorry for my bad English.
Example:
I must will do next operations:
1. To check that all dimensions contain the correct values (not manually changed).
2. To check all text and multileader objects on compliance to a rule (regular expression) and if will be found - to do necessary changes (replace old values on new values by other regular expression).
3. To check that all text, the sizes, multileaders and hatches are on the necessary layers.
4. etc.

For the solution of these tasks, I want to lock  Database for external changes, then having executed once iteration - to create the dictionary Dictionary<string, List<ObjectId>>. Average speed of obtaining such dictionary - 1-1,5 second for 1 000 000 objects (for my computer). But then, having received the objects of ObjectId grouped in type, it will be simpler to me to carry out the tasks. For example, it is convenient to use LINQ for collection processing. After task performance I will unlock Database.

MexicanCustard

  • Swamp Rat
  • Posts: 705
Why not perform those task on the first iteration?  Why iterate to obtain ObjectIds just so you can iterate them again?  You could find out the object type from the ObjectId and if it's an object you are looking for, do what you want to it then.
Revit 2019, AMEP 2019 64bit Win 10

Andrey Bushman

  • Swamp Rat
  • Posts: 864
2 MexicanCustard
May be you are right, but I will leave this method just in case (cases be are different).

2 TheMaster
What about my questions?

I received the answer from ADN, but unfortunately their code works incorrectly. Probably I was misunderstood by developers of Autodesk. I reported to them about it.