Result of the Tests
As you know by now, trusting test results is a risky business. So many things are different between the tests and the test environment compared to your own environment, so you might get completely different results yourself. Anyway, I find it interesting to publish my results, and you can use them as a starting point for your own tests. That is also the case with the code, of course.
I said earlier that I used DataReader as a baseline. Therefore, I recalculated all the values so that I get a value of 1 for DataReader and so that the rest of the data containers will have a value that is relative to the DataReader value. That makes it very easy to compare. The higher the value is, the better. To avoid showing you only results with the 1 digit, I have also added a sneak peek at the untyped DataSet in the tables.
Table 1: Read One Row
1 User, in AppDomain |
5 Users, in AppDomain |
1 User, Cross-Machines |
5 Users, Cross-Machines |
|
DataReader |
1 |
1 |
1 |
1 |
Untyped DataSet |
0.6 |
0.6 |
1.4 |
1.7 |
Table 2: Read Many Rows
1 User, in AppDomain |
5 Users, in AppDomain |
1 User, Cross- Machines |
5 Users, Cross- Machines |
|
DataReader |
1 |
1 |
1 |
1 |
Untyped DataSet |
0.5 |
0.6 |
6.9 |
9.7 |
Table 3: Read One Master Row and Many Detail Rows
1 User, in AppDomain |
5 Users, in AppDomain |
1 User, Cross-Machines |
5 Users, Cross-Machines |
|
DataReader |
1 |
1 |
1 |
1 |
Untyped DataSet |
0.5 |
0.5 |
6.1 |
8.5 |
To explain the results a bit with an example, take a look at Table 3. In the single-user test, the untyped DataSet is half as well performing as the DataReader within the AppDomain, but it is more than six times as well-performing cross-machines. This gives a good idea of the cost of marshalling by reference of the DataReader, across AppDomain boundaries.
NOTE
"In AppDomain" is pretty similar to what we talked about as "in-process" before .NET.
It's pretty early to give any grades because we have only covered the DataReader, but this will show what you can expect from the coming articles. In Table 4, I have given some highly subjective results according to the qualities discussed at the beginning of the article. A score of 5 means great, and a score of 1 means bad.
NOTE
Even if you agree with me in Table 4, you should add some weights so that the qualities most important for you have a higher weight than the others.
Table 4: Grades According to Qualities
Performance in AppDomain/Cross-Machines |
Scalability in AppDomain/Cross-Machines |
Productivity |
Maintainability |
Interoperability |
|
DataReader |
5/1 |
4/1 |
2 |
1 |
1 |
Now I'll say a few words about each quality grade.
Performance
As you saw before, the performance is great within an AppDomain, but it's just the opposite if the DataReader has to "travel" between AppDomains, possibly cross-machines. The reason for the bad performance cross-AppDomains is, of course, that DataReaders are marshalled by reference and, therefore, actually won't travel at all. That means that all calls to a DataReader will lead back to the AppDomain where it was created.
NOTE
Even if, against all recommendations, you decide to ship DataReaders across AppDomain boundaries, watch out for what methods you use. For example, GetSqlMoney() won't work because it returns a datatype that isn't marked with the Serializable attribute.
Scalability
Scalability is pretty similar to performance when we discuss DataReaders. One thing to consider is that you must be extremely carefully so that you don't do long-running operations during a browse through a DataReader. Then you will hurt scalability instead because the connection will be occupied until the DataReader has been read through (or closed). Anyway, I think the DataReader should have a 5 for scalability in AppDomain, as long as it is used wisely. However, the risk is large enough that you will hold on to the DataReader and its connection too long and, therefore, hurt scalability. That's the reason for a 4.
NOTE
It's a bit awkward to say whether a DataReader in itself is scalable. The scalability is only about how the DataReader is used.
Productivity
Using DataReaders as the primary programming model means quite a lot of work if you manually write all the code. You should probably write yourself a generator that writes the code for you.
Maintainability
To be honest, I don't see DataReaders as a good choice for simulating a data container. The main problem is maintainability. I don't think the client should have such a direct reference in to the database. This will make it much harder, for example, to change the database structure in the future. In my opinion, DataReaders are useful for dragging data from the database as fast as possible and filling another disconnected data structure.
NOTE
I'm pretty sure that I will get some comments about this article saying that no real architect would dream of using a DataReader as a data container. I know, I know.
Interoperability
Another big problem with DataReaders is that they aren't interoperable. Try to hand out a DataReader to a client running Linux, for example, and wait for flaming e-mails.