As I mentioned in the previous post, Codus essentially generates a Data Access Object (DAO) and a Transfer Object (TO) for each table or view you choose to map in your database. Each generated DAO basically implements the following 4 CRUD-type methods and 1 mapping method, which are abstract methods of the base class, AbstractDAO:
So when you look at your generated DAO for each table or view you chose to “map,” the code is quite small and easy to understand. A lot of the functionality that uses these methods is packed into the AbstractDAO class that is part of the Adapdev framework.
Let’s take a peek at one such method, SelectAll(). As you would expect, SelectAll() grabs all the records from a particular table or view and returns an ArrayList, which implements IList. So, to get all the addresses in the addresses table, we do the following:
The SelectAll() method is actually implemented by the AbstractDAO base class as shown below.
The bulk of the work is done within the call to MapObjects, which we will talk about in a second. These methods go out and construct a proper “Select * from [Address]” query and a DataReader for the appropriate database type. As an aside, notice the C# using statement in the SelectAll() method. IDbConnection implements IDisposable, and the using statement in C# is a nice way to make sure all unmanaged resources are cleaned up after we extract the data from the database. The using statement essentially adds a try/finally block and calls Dispose() under the covers so you don’t have to (VB.NET 2005 has the using keyword now as well). For more information on implementing the using statement with ADO.NET, you can read the following post: C# Using Statement – Try / Finally – IDisposable.
However, IDataReader also implements IDisposable, and at first glance, I would like to see a using statement around IDataReader as well. It would look something like this:
One thing worth mentioning is the this.CreateSelectAllCommand(). Sean has this pretty cool method of wrapping queries using a query object that essentially eliminates the need for creating the SQL. The idea here is to construct a query in more of an OOP methodology using methods and properties on a Query object, and then calling the object’s GetText() method to emit the proper SQL for the database type. I would call this a good example of the Builder Design Pattern. The Builder Design Pattern stores the data in an intermediate object (ISelectQuery) until the application is ready to ask (GetText()) the storage object to construct the target object (SQL). Very nice!
I suggested that Eric do something similar to this with his EasyAssets architecture, and I think he can certainly use Sean’s implementation for inspiration if he considers it at a later date. Although only very slightly related, I also talked briefly about how Community Server uses a BlogPostQuery object to pass parameters, which has a slight (very slight) similar look and feel. You can read about that in Community Server Source Code – Abstract Classes, Reflection and Data Providers.
As mentioned above, the MapObjects method does most of the work. It iterates through the DataReader and calls the MapObject(IDataReader dr) method that is generated by Codus for each specific table and view you chose in the GUI:
The MapObject method does the data mapping and object creation.
That’s pretty clean and well-refactored code! SelectAll() is a pretty trivial example, but most of the underlying methods implemented by AbstractDAO share this same concept, which makes this O/R Mapper really easy to maintain.
One of the things I would like to see is record paging. It does SelectAll() and SelectAllWithLimit(int maxRecords), but I don’t see a paging mechanism that allows you to choose records for a particular page index. You can certainly add it yourself, but this is something so basic that it should be added to the AbstractDAO class. Great tool! Now on to another open source project