Click here to Skip to main content
Email Password   helpLost your password?

Introduction

In a perfect system there is no reason for maintenance. Everything is well designed from the onset. Stored Procedures never change their return cursors and data is always of the right size and type. However, in the real-world, things change. Even more importantly, in the real-world you will work with multiple developers. Throw in a good helping of corporate and cultural communication barriers and you have a recipe for developing code the quickly becomes unmanageable. This article is written specifically for accessing fields from rows from a dataset and a small subset of difficulties that crop up.

The problem

Accessing row based data in C# against any database is a fairly simple task.

foreach(DataRow row in table.Rows){
    customer = new Customer();
    customer.ID = (Int32)row[0];
    customer.FirstName = row[1].ToString();
    customer.LastName = row[2].ToString();
}//end foreach

This is fast and easy. It is also very common as I see it implemented fairly regularly.

On a single developer team/small project this does not present a problem. However, consider the larger case. The business group at the Fortune 500 you are working for has just decided that MiddleName is a required field in the database. The dba adds the field and changes the stored procedures and now your code does not work. Even worse, it does not fail.

A Solution

Finally the email gets distributed and the responsible team spots the error immediately. The new code is written as follows:

foreach(DataRow row in table.Rows){
    customer = new Customer();
    customer.ID = (Int32)row["customerID"];
    customer.FirstName = row["firstName"].ToString();
    customer.LastName = row["lastName"].ToString();
}//end foreach

Unfortunately, this is 2 orders of magnitude slower! In most cases no one will ever notice. But in other cases, this will stand out like a *insert lame clich� here*

Another Solution

After staying up for weeks straight and rewriting stored procedures to use fully qualified names and tweaking bits of logic there is still more to be saved in the application. Looking back at the for loop to load 50,000 customers into some customer objects it becomes obvious that there is another way:

int customerIDIndex = table.Columns.IndexOf("customerID");
int customerFirstNameIndex = table.Columns.IndexOf("firstName");
int customerLastNameIndex = table.Columns.IndexOf("lastName");

foreach(DataRow row in table.Rows){
    customer = new Customer();
    customer.ID = (Int32)row[customerIDIndex];
    customer.FirstName = row[customerFirstNameIndex].ToString();
    customer.LastName = row[customerLastNameIndex].ToString();
}//end foreach

Possible Problems

In any sufficiently large situation it is possible for code to be out of sync with the database. Especially with many companies now applying SOX standards to even the most trivial data. With this in mind there is one more step to increase the maintainability of this code. Throwing in some assertions will give any debugger immediate access to the logical root of all evil, rather than spending an hour deciphering "friendly" error messages.

int customerIDIndex = table.Columns.IndexOf("customerID");
int customerFirstNameIndex = table.Columns.IndexOf("firstName");
int customerLastNameIndex = table.Columns.IndexOf("lastName");

System.Diagnostics.Debug.Assert(customerIDIndex > -1, 
"Database out of sync"); System.Diagnostics.Debug.Assert(customerFirstNameIndex > -1,
"Database out of sync"); System.Diagnostics.Debug.Assert(customerLastNameIndex > -1,
"Database out of sync"); foreach(DataRow row in table.Rows){ customer = new Customer(); customer.ID = (Int32)row[customerIDIndex]; customer.FirstName = row[customerFirstNameIndex].ToString(); customer.LastName = row[customerLastNameIndex].ToString(); }//end foreach

Conclusion

There are likely other solutions. Relying on better error handling will even remove the need for the assertions. However, when writing maintainable code, you have to code defensively. Hopefully this small article has illustrated a method of defensive programming that also keeps intact that notion of efficient coding.

As a small but relevant aside, it is also possible to do the following:

int customerIDIndex = table.Columns.IndexOf("customerID");
int customerFirstNameIndex = table.Columns.IndexOf("firstName");
int customerLastNameIndex = table.Columns.IndexOf("lastName");

foreach(DataRow row in table.Rows){
    customer = new Customer();
    if(customerIDIndex > -1)
        customer.ID = (Int32)row[customerIDIndex];
    if(customerFirstNameIndex > -1)
        customer.FirstName = row[customerFirstNameIndex].ToString();
    if(customerLastNameIndex > -1)  
        customer.LastName = row[customerLastNameIndex].ToString();
}//end foreach

But I would not recommend this as it just masks the error and creates many unnecessary if statements.

Points of Interest

A good place to find answers to hard problems: Google
You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
JokeThis is revolutionary
joelhegeman
7:39 7 May '09  
Wow, this is amazing
GeneralRe: This is revolutionary
Ennis Ray Lynch, Jr.
8:02 7 May '09  
I knew that name sounded familiar. What are you up to?


Need custom software developed? I do C# development and consulting all over the United States.

A man said to the universe:
"Sir I exist!"
"However," replied the universe,
"The fact has not created in me
A sense of obligation."
--Stephen Crane


GeneralWhat about using a generated typed DataSet ?
hleuze
5:38 10 Aug '06  
the code to access the columns will look more or less like this:

foreach(CustomerRow row in table.Rows){
customer = new Customer();
customer.ID = row.CustomerID;
customer.FirstName = row.CustomerFirstName;
customer.LastName = row.CustomerLastNameIndex;
}//end foreach


With a typed dataset you get the following advantages:
-The syntax to access the columns is more readable
-no need to type-cast (this is done in the generated code)


greetings
heiko


GeneralRe: What about using a generated typed DataSet ?
Ennis Ray Lynch, Jr.
6:00 10 Aug '06  
Have you done a profile on the execution of type datasets?

Personally, I like creating strongly typed objects to match my data. Unfortunately, typed datasets, are even less maintainable in a large corporate setting. It is very easy for novice programmers to directly edit generated code even in the presence of the warning comment. I have spent hours matching XSD's to classes only to have, less than a week later, it unsynced. I was not very understanding.





A man said to the universe:
"Sir I exist!"
"However," replied the Universe, "The fact has not created in me A sense of obligation."

-- Stephen Crane

GeneralRe: What about using a generated typed DataSet ?
hleuze
23:17 10 Aug '06  
Yes, you are right ... Using a dataset is not the fastest way to get data from a database. But they have advantages if you want to bind data to the UI, doing filtering, sorting etc on the client.
As always, it depends what you want/need to do with the data !

By the way, there is a nice article on msdn that compares the different techniques available in DotNet. It has the title "Performance Comparison: Data Access Techniques" and you can find it looking here:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/bdadotnetarch031.asp[^]

The article was written in Jan, 2002 (-> DotNet Version 1.1), so maybe populating a dataset has become faster in DotNet V 2.0 Laugh


QuestionNice but ...
Sébastien Lorion
20:13 7 Aug '06  
Your idea is fine but if you care about performance, then why load data in a DataTable to later duplicate it again in custom business entities ? Skip the middle man and use a data reader!

I would even go further: if you care about performance, stay as far as you can from DataTable, especially in .NET 1.1

If you still want to use DataTable, then you can make your business entities wrap them (inheritance or composition) and use a DataAdapter with ColumnMapping. You can then set its MissingMappingAction and or MissingSchemaAction to Error and have the desired behavior.

Sébastien



Intelligence shared is intelligence squared.

Homepage : http://sebastienlorion.com

AnswerPersonally
Ennis Ray Lynch, Jr.
20:28 7 Aug '06  
I hate datatables. But we don't always get to use what we want. BTW, the same should apply for a datareader as well.



A man said to the universe:
"Sir I exist!"
"However," replied the Universe, "The fact has not created in me A sense of obligation."

-- Stephen Crane

GeneralRe: Personally
Chris S Kaiser
8:57 9 Aug '06  
Are you mandated to use DataTables? I have to agree with Sébastien, DataReaders are the way to go, especially if you need to populate your own business objects. DataSet and DataTable are basically an in memory database with all the fat and trimmings. While the DataReader simply marshalls the data from the server.

This statement is false.

GeneralUnf, yes
Ennis Ray Lynch, Jr.
12:48 9 Aug '06  
In fact the interface I am forced to use for database access only returns datatables. I can't even trick it to give me a datareader. I keep asking for permission to modify it but it is one of those religious sections of code that several projects reference and that crashes everything when the versio number changes, dont ask Smile



A man said to the universe:
"Sir I exist!"
"However," replied the Universe, "The fact has not created in me A sense of obligation."

-- Stephen Crane

GeneralRe: Unf, yes
Chris S Kaiser
12:58 9 Aug '06  
o u c h... D'Oh! well then... good luck! Smile

This statement is false.

GeneralRe: Nice but ...
Andrew Rissing
5:41 8 Aug '06  
In general, yes - .NET 1.1 has troubles with DataTables. But personally, I've found vast performance enhancements with the .NET 2.0 typed DataSet.

With its indexing and autogenerated methods, it makes maintenance and development a breeze. We were able to put all of our Business Rules into the DataSet, which simplified a lot of our logic as well. Granted, we're working with a data repository of sorts where we do lots of post-processing of the data to offload the database's work.

Obviously, you use the best tool for the job, but for us...it is definitely the typed Dataset. At some point in time, the small bit of performance we aren't getting is not worth the hassles of creating/maintaining a data object that would just duplicate the DataTable.

Personally, I've just got no complaints about DataTables in .NET 2.0.
AnswerRe: Nice but ...
Sébastien Lorion
6:15 8 Aug '06  
Humm ... do some profiling of the memory usage in 2.0 and you will see that it is has not improved at all from 1.1 : the same steep staircase pattern. This means that if you do a web application which has a medium to high volume, you either need to be really careful and do a lot of code review, use another solution or buy a (many) bigger server(s).

Sébastien



Intelligence shared is intelligence squared.

Homepage : http://sebastienlorion.com

GeneralRe: Nice but ...
Andrew Rissing
6:39 8 Aug '06  
Ennis, I'll take this sidetopic off to emails to save your article from being spammed. Wink


Last Updated 1 Aug 2006 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010