LINQ and the AutoCAD .NET API (Part 3)
From a LayerTable to SymbolTables
This is the third in a series of posts on LINQ and the AutoCAD .NET API. Here's a complete list of posts in this series.
Introduction
This is part three of a series of posts on LINQ and the AutoCAD .NET API. In part 2 we stopped with the following implementation of our LayerHelper
:
Listing 1: LayerHelper
Generalization
The idea was to make the LayerHelper
more general, to not only provide access to layers, but to other tables as well:
Listing 2: MyCommand
Right now we only have GetLayers()
implemented. But it's rather easy to implement - as an example - GetBlocks()
on the GeneralHelper
, because the code to get all blocks from the database is actually the same like for layers:
Listing 3: GetBlocks
Tables
If we compare the GetLayers()
and GetBlocks()
methods, we can easily see that dealing with tables has the following pattern:
- The table that contains all records is of type
{Item}Table
- Each item in the table is of type
{Item}TableRecord
- The ID for the table object can be obtained from a property of the database object called
{Item}TableId
And {Item}
is (according to the ObjectARX Managed Class Reference Guide) one of the following names: Block
, DimStyle
, Layer
, Linetype
, RegApp
, TextStyle
, Ucs
, Viewport
, View
. Put together we get the following:
Table type | Record type | ID property |
---|---|---|
BlockTable | BlockTableRecord | BlockTableId |
DimStyleTable | DimStyleTableRecord | DimStyleTableId |
LayerTable | LayerTableRecord | LayerTableId |
LinetypeTable | LinetypeTableRecord | LinetypeTableId |
RegAppTable | RegAppTableRecord | RegAppTableId |
TextStyleTable | TextStyleTableRecord | TextStyleTableId |
UcsTable | UcsTableRecord | UcsTableId |
ViewportTable | ViewportTableRecord | ViewportTableId |
ViewTable | ViewTableRecord | ViewTableId |
It's important to notice that all tables are derived from SymbolTable
and all table records from SymbolTableRecord
. Therefore we can easily turn GetLayers()
into a generic GetItems()
method, that takes the table ID as an argument:
Listing 4: GetItems
Looks good! But there's still room for optimization: the only thing we do with the table is that we iterate the IDs. So there has to be some enumerable interface in play. A closer look into the ObjectARX Managed Class Reference Guide tells us, that the base class SymbolTable
implements IEnumerable
. So it should be OK to simply cast the table object we get from the transaction to IEnumerable
. Hence we can remove the generic parameter for the table. This makes the method much simpler:
Listing 5: GetItems
Alright! Now let's put everything together: at first we rename the LayerHelper
to GeneralHelper
. Then we add the generic GetItems()
method and inflate the class a bit by adding one "wrapper" method for each table type:
Listing 6: GeneralHelper
Voila! We now have a GeneralHelper
that fully supports what we intended in the client code in listing 2. Still another small optimization can be done: if we change the public Get methods to properties, we get a more intuitive way to access the items:
Listing 7: GeneralHelper
Bye-bye boilerplate
And now we can unleash the real beauty and power of LINQ! Let's write a command that displays all layer names that have the name prefix "ABC_":
Listing 8: DisplayAbcLayers
Awesome. The expressiveness of a query like the one in line 6 is just unbeatable! And no boilerplate code involved! The only "setup" we do is to create the helper inside a using statement. And what follows is just business logic. Yeah!
Admittedly, this example is trivial, but still much easier to read and comprehend than the one we started from in part 1.
But there's more
We've come pretty far, but there's still a lot we can do. By now we've covered all tables in the AutoCAD drawing database, but many other interesting items are stored in another main category of AutoCAD containers: dictionaries. In the next post we'll extend the GeneralHelper with dictionary accessors and do some further refactorings.