This is part 8 in our series. Today we want to apply the changes that we made to tables of type SymbolTable to containers of type DBDictionary.
Let's recap how objects are persisted in the AutoCAD database. In general we deal with tables and dictionaries:
Tables
Dictionaries
Container Type
SymbolTable
DBDictionary
Item Type
SymbolTableRecord
DBDictionaryEntry
IEnumerable Item Type
ObjectId
DBDictionaryEntry
Each table is derived from SymbolTable, each dictionary form DBDictionary. And each item in a table is derived from SymbolTableRecord, each item in a dictionary is derived from DBDictionaryEntry. Very interesting are the entries we get when we iterate the table or dictionary: when we iterate a SymbolTable, each table entry is the ObjectId of the associated SymbolTableRecord. When we iterate a DBDictionary, each dictionary entry is a DBDictionaryEntry. A DBDictionaryEntry is a key-value-pair, having the ObjectId as the value. We need this detail later for our implementation.
Creating objects II
In last post's listing 6 we created an abstract base class ContainerBase that wraps the access to an AutoCAD table. We will show the listing here again, but we rename the class to TableContainerBase to avoid difficulties to distinguish our classes:
Now we want to apply the structure in this class to dictionaries as well. To do that, we first simply copy the code from the TableContainerBase class, adjust it to DBDictionaries and DBDictionaryEntries and finally put the common parts of the two classes into a common base class.
OK, let's start. We copy and make some changes, such that we can use the class with a DBDictionary:
Finding a base class
Our next step is to take out the common parts and put them in a base class called ContainerBase. GetEnumerator is similar but not the same in the two classes. The iterator in DictionaryContainerBase returns items of type DBDictionaryEntry (which, as we said before, have the ObjectId of the actual item in a property named Value), whereas the items of a table iterator are simply the ObjectIds of the BlockTableRecords. As we are only interested in the ObjectId, we could introduce a method GetObjectId(object) that takes the iterator item as an argument and returns the ObjectId:
Looks good. We now have our base class and delegated discovering the ObjectId to the derived classes.
The next method to look at is IsValidName(string). I'm not sure if there are different naming constraints for tables and dictionaries, but for the moment we assume that names for table entries and dictionary entries are the same. So we can add IsValidName(string) to the base class, as it is exactly the same in both derived classes.
When we look at Contains(string), we see that there is a difference in the two variants. A SymbolTable has a Has(string) method to check the existence of an item, a DBDictionary has a Contains(string) method to do that. So nothing that we can make reusable in the base class.
Last but not least Create(string). Most of the code is similar, but handling the name of the item is different as well as adding it to the table/dictionary. We could introduce a method named AddItem(string, T) that delegates adding items to the derived classes.
Let's sum up our findings:
IsValid(string) comes into the base class
Contains(string) is different in both cases and is made abstract in the base class
Create(string) has some commonalities, the differences are delegated to the derived classes by introducing AddItem(string, T)
With that in mind, we now extend the class from listing 3:
Looks like a nice base class, we can reuse most of the code.
Building the derived classes
Now let's look at the derived classes. What's left for TableContainerBase<T>?
TableContainerBase<T> is now reduced to the minimum. One thing to mention is that we use the sealed keyword to prevent the methods from being overridden in derived classes.
Next we have a look at DictionaryContainerBase<T>:
Almost the same as TableContainerBase<T>: reduced to a minimum, only the DBDictionary depended stuff is left.
And how do these changes affect the GeneralHelper class? The first thing we have to change is that all the table container classes (like BlockContainer) now derive from TableContainerBase instead of ContainerBase.
The other thing is that we now have to implement the classes that derive from DictionaryContainerBase. For example the container class for Layouts looks like the this:
And, if we apply this to all dictionaries, we finally have the following GeneralHelper class:
Wrapping up
Finally, the new GeneralHelper class allows us to use the same functionality for dictionaries as well:
The main improvement to the GeneralHelper class is that we now have a dedicated container class for all the tables and the dictionaries as well. The implementation details are encapsulated in the container classes.
In the next post we further refactor the GeneralHelper class.