000

Index Labels

Tips of Using LINQ in AutoCAD .NET programming

.
If you target your AutoCAD .NET API programming at .NET3.5 or 4.0, using LINQ would sometimes make your coding a lot easier. Typically, writing code to loop through various collection objects in drawing database is one of mostly encountered tasks of AutoCAD programmers.

I demonstrate here some sample code that uses LINQ (to object) to make the code simpler and easier.

BlockTableRecord class is one the mostly used class an AutoCAD programmer may have to deal with. It implements IEnumerale interface. So, we have seen a lots of "foreach(...)" loop used against it in AutoCAD .NET code, especially when ModelSpace BlockTableRecord is opened for searching certain type of entities contained in ModelSpace. Here are some cases.

Case 1:  searching certain types of entities in BlockTableRecord (ModelSpace, PaperSpace, or a block definition).

private List<ObjectId> GetCertainEntityIDs(Database db)
{
    List<ObjectId> ids = null;
 
    using (Transaction tran = db.TransactionManager.StartTransaction())
    {
        BlockTable tbl = 
            (BlockTable)tran.GetObject(db.BlockTableId, OpenMode.ForRead);
 
        //Get modelspace BloclTableRecord
        BlockTableRecord br = 
            (BlockTableRecord)tran.GetObject(tbl[BlockTableRecord.ModelSpace], OpenMode.ForRead);
 
        //Cast the BlockTableRecord into IEnumeralbe<T> collection 
        IEnumerable<ObjectId> b = br.Cast<ObjectId>();
 
        //==============search certain entity========================//
        //"LINE" for line
        //"LWPOLYLINE" for polyline
        //"CIRCLE" for circle
        //"INSERT" for block reference
        //...
        //We can use "||" (or) to search for more then one entity types
        //============================================================//
 
        //Use lambda extension method
        ids = b.Where(id => id.ObjectClass.DxfName.ToUpper() == "LINE" || 
            id.ObjectClass.DxfName.ToUpper() == "LWPOLYLINE").ToList<ObjectId>();
 
        //Use LINQ statement. This is more readable
        ids = (from id in b
                where id.ObjectClass.DxfName.ToUpper()=="LINE" ||
                        id.ObjectClass.DxfName.ToUpper() == "LWPOLYLINE"
                select id).ToList<ObjectId>();
 
        tran.Commit();
    }
 
    return ids;
}

Case 2: often, we need to get a distinctive entity type list from a drawing (e.g. we need to know what types of entities the drawing's model space contains). Following code get a string array of entity types from model space.

private static string[] GetEntityType(Document dwg)
{
    string[] types = null;
 
    Database db = dwg.Database;
    using (Transaction tran = db.TransactionManager.StartTransaction())
    {
        BlockTable tbl = 
            (BlockTable)tran.GetObject(db.BlockTableId, OpenMode.ForRead);
 
        //Get modelspace BloclTableRecord
        BlockTableRecord br = 
            (BlockTableRecord)tran.GetObject(tbl[BlockTableRecord.ModelSpace], OpenMode.ForRead);
 
        //Cast the BlockTableRecord into IEnumeralbe<T> collection
        IEnumerable<ObjectId> b = br.Cast<ObjectId>();
 
        //Use lambda extension method
        types = b.Select(id => id.ObjectClass.DxfName).Distinct().ToArray();
 
        //Use LINQ statement. This is more readable
        types = (from id in b select id.ObjectClass.DxfName).Distinct().ToArray();
 
        tran.Commit();
    }
 
    return types;
}

Case 3: getting a name list of blocks that have been inserted into model space, or get all block references or block references with given names in model space.

From the code show above, we can see ObjectClass property of ObjectId struct, which was only made available since AutoCAD 2009, helps a lot in defining searching condition (in Where() or Distinct() extension methods). In this scenario, we need a extra method to get to entity itself. Here is it:

private static BlockReference GetBlockRef(ObjectId id, Transaction tran)
{
    return (tran.GetObject(id, OpenMode.ForRead) as BlockReference);
}

Now we can get a name list of all inserted blocks:

private static string[] GetBlockNameList(Document dwg)
{
    string[] names = null;
 
    Database db = dwg.Database;
    using (Transaction tran = db.TransactionManager.StartTransaction())
    {
        BlockTable tbl = 
            (BlockTable)tran.GetObject(db.BlockTableId, OpenMode.ForRead);
 
        //Get modelspace BloclTableRecord
        BlockTableRecord br = 
            (BlockTableRecord)tran.GetObject(tbl[BlockTableRecord.ModelSpace], OpenMode.ForRead);
 
        //Cast the BlockTableRecord into IEnumeralbe<T> collection
        IEnumerable<ObjectId> b = br.Cast<ObjectId>();
 
        //Use lambda extension method
        names=b.Where(id=>id.ObjectClass.DxfName.ToUpper()=="INSERT")
                .Select(id=>GetBlockRef(id,tran).Name).Distinct().ToArray();
 
        //Use LINQ statement. This is more readable
        names = (from id in b
                where id.ObjectClass.DxfName.ToUpper().Contains("INSERT")
                select GetBlockRef(id, tran).Name).Distinct().ToArray();
 
        tran.Commit();
    }
 
    return names;
}

To search blocks with given name, we do this:

private static List<BlockReference> GetBlockRefs(string blkName, Document dwg)
{
    List<BlockReference> blks = null;
 
    Database db = dwg.Database;
    using (Transaction tran = db.TransactionManager.StartTransaction())
    {
        BlockTable tbl = 
            (BlockTable)tran.GetObject(db.BlockTableId, OpenMode.ForRead);
 
        //Get modelspace BloclTableRecord
        BlockTableRecord br = 
            (BlockTableRecord)tran.GetObject(tbl[BlockTableRecord.ModelSpace], OpenMode.ForRead);
 
        //Cast the BlockTableRecord into IEnumeralbe<T> collection
        IEnumerable<ObjectId> b = br.Cast<ObjectId>();
 
        //Use lambda extension method
        blks = b.Where(id => id.ObjectClass.DxfName.ToUpper()=="INSERT")
            .Select(id => GetBlockRef(id, tran)).ToList<BlockReference>();
 
        //Use LINQ statement. This is more readable
        blks = (from id in b
                where id.ObjectClass.DxfName.ToUpper()=="INSERT"
                select GetBlockRef(id, tran)).ToList<BlockReference>();
 
        tran.Commit();
    }
 
    //return only blockreferences with name starting with blkName with lambda extension method
    return blks.Where(b=>b.Name.ToUpper()==blkName.ToUpper()).ToList();
 
    //Or return with LINQ statement
    return (from b in blks where b.Name.ToUpper()==blkName.ToUpper() select b).ToList();
}

Note, the last code sample "GetBlockRefs()" is only for demonstrating the use of LINQ. It may not be a good idea in certain situation to return entity instead of ObjectId outside a transaction.

As you can see, with the help of LINQ, we can write more readable code easily to search drawing database. However, what is the difference in term of code execution speed between simply looping through the model space with foreach (...) and using LINQ as I showed here? I did not look into this, honestly.I did run the code with drawing with a few thousands entities in model space and did not feel speed difference. But I assume there is difference with LINQ code might result in slightly slower speed: when casting BlockTableRecord into IEnumerable, a loop must have been done behind the scene. If you have very big model with hundreds of thousands entities, you want to test if the LINQ code actually slows down the execution noticeably.

Blog Archive

Labels

3D Modeling 3D Sketch Inventor AI Design AI in Manufacturing AI Tools Architecture Artificial Intelligence AutoCAD AutoCAD advice AutoCAD Basics AutoCAD Beginners AutoCAD Civil3D AutoCAD commands AutoCAD efficiency AutoCAD features AutoCAD File Management AutoCAD Layer AutoCAD learning AutoCAD print settings AutoCAD productivity AutoCAD Teaching AutoCAD Techniques AutoCAD tips AutoCAD training. AutoCAD tricks AutoCAD Tutorial AutoCAD workflow AutoCAD Xref Autodesk Autodesk 2025 Autodesk AI Tools Autodesk AutoCAD Autodesk Fusion 360 Autodesk Inventor Autodesk Inventor Frame Generator Autodesk Inventor iLogic Autodesk Recap Autodesk Revit Autodesk Software Autodesk Video Automation Automation Tutorial Basic Commands Basics Beginner Beginner Tips BIM BIM Implementation Block Editor ByLayer CAD comparison CAD Design CAD File Size Reduction CAD line thickness CAD Optimization CAD Productivity CAD software clean CAD file cleaning command Cloud Collaboration command abbreviations Construction Technology Contraints Create resizable blocks CTB STB Data Reference Data Shortcut design software Design Workflow Digital Design Digital Twin Drafting Standards Drawing Automation Dref Dynamic Block Dynamic Block AutoCAD Dynamic Blocks Dynamic doors Dynamic windows eco design editing commands energy efficiency Engineering Engineering Design Engineering Innovation Engineering Technology engineering tools Excel Express Tools External Reference Fast Structural Design Fusion 360 Generative Design green building Grips heavy CAD file Heavy CAD Files iLogic Industry 4.0 Insight Inventor API Inventor Drawing Template Inventor Frame Generator Inventor Graphics Issues Inventor IDW Inventor Tips Keyboard Shortcuts Learn AutoCAD Machine Learning in CAD maintenance command Management Manufacturing Innovation Metal Structure ObjectARX .NET API Organization OVERKILL OVERKILL AutoCAD Palette PDF Plot Style AutoCAD Practice Drawing Printing Quality professional printing Professional Tips PTC Creo PURGE PURGE AutoCAD ReCap reduce CAD file size Resizable Block Revit Revit Best Practices Revit Workflow Ribbon screen shortcut keys Shortcuts Siemens NX Sketch Small Firms Smart Block Smart Factory SolidWorks Steel Structure Design sustainability Sustainable Manufacturing toolbar Tutorial User Interface (UI) Workbook Workspace XLS Xref