Recently I was involved in a discussion on creating BusinessObjects, BusinessObject Collections, and where one should keep the code to create the objects.
In a N-tiered system, you want to create seperate tiers. While most people can quote the name of the tiers, understanding them and actually keeping them seperated is the fall short area.
I have created a sample which has a:
Presentation Tier ( a Console App )
I am piggy-backing off the NorthWind database for my example.
I have created 2 strong objects (Customer and Order), and 2 Collection objects (CustomerCollection and OrderCollection).
I have also created a CustomerController object to handle the creation of Customer’s and Order’s.
The CustomerController is a business object, which calls the DataLayer object, and gets an IDataReader to populate the data.
(Sql Server 2000/7.0/2005 scripts are included in the download as well).
I am also using the DAAB 2.0 "SQLHelper" class to assist with the datalayer object. While I use the EnterpriseLibrary project now in my production code, the SQLHelper is good for this demo, as it is Sql Server (2000 usually) specific. It also provides an easier mechanism for implementing the connection string property.
However, it points out the fact that I should be able to move from one DataHelper (SQLHelper) to another (EnterpriseLibrary.Data) and *never* have to touch/recompile the
Presentation Code or the
In fact, you could switch from Sql Server to Oracle, and never have to recompile these 2 upper tiers (again, Presentation and BusinessLogic should be isolated from the database vendor of choice)
Hopefully the sample will provide an illustration to avoid common mistakes.
Those mistakes are:
1. Intertwined business layer logic and database connectivity.
2. Substituting the SQLHelper class ~in place of~ the application’s DataLayer object. The SQLHelper (as the name implies) is there to assist/help the DataLayer object, not replace it.
3. Keeping objects (their properties, methods and sometimes events) seperate from the code which creates them.
I show how to create a sub collection of Orders per Customer, and how to accomplish this with one database call. (IDataReader.NextResult();)
I show how the DataAccessLayer should be simple, and pretty much return only data.
My rule of thumb is that:
The DataLayer object should return:
DataSet’s (typed and untyped)
Scalars ( count(*) is an example here)
void/Nothing (aka, you just call a stored procedure which does something, but does not return anything)
That’s it. That’s what the DataLayer should return.
The obvious reason is that any RDBMS or datastore should be able to return DataSet’s and IDataReaders. That’s the goal, to provide data to the business layer without having intricate knowledge of the backend database.
As a bonus, I include some time-test code to compare:
Custom Objects/Collections being populated with an IDataReader
Typed DataSet object, with Constraints enabled.
Typed DataSet object, with no Constaints (in the xsd definition)
You can view the results.
In regards to Custom Objects/Collections vs Typed DataSet’s vs other methods:
Like in most areas of software development, there are not "blanket statements" for which method to use.
There are pro’s and con’s, and it depends.
I usually go with the CustomObjects/Collections.
I jokingly refer to DataSet’s as the "poor man’s business object". However, there are times when they make good sense.
You can download the example code here. ("Right Click"/"Save Target As" is the best approach I think)
I am working up a DotNet 2.0 example also, where I replace the CollectionBase objects with generics. Look for that code soon.
And a reference to read from start to finish, aka, very informative for a bird’s eye view: