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:
Generalization
The idea was to make the LayerHelper more general, to not only provide access to layers, but to other tables as well:
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:
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:
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:
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:
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:
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_":
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!
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.