IHeart Interfaces - Part One: IDataReader and IDataRecord

by Chad Finsterwald

This code sample applies to ASP.NET in the .NET 2.0 framework. Examples are written in C#. Download the code

Contents

  1. Introduction
  2. IDataReader (and DbDataReader)
  3. IDataRecord
  4. Conclusion

Introduction

Note: This article assumes that you have a basic grasp of interfaces, abstract classes, polymorphism, and of OO programming. If you are not familiar with these concepts, I have listed below a few good resources to get you started.

You've likely heard the principle Program to the interface, not the implementation. Just about every book on application design that I've read covers this idea at some point and often devotes a chapter or two to its exploration. As is often the case, however, there is a wide gap between knowing the principle and knowing how to put it into practice. This gap reveals itself not only in our own class and application design, but often in our use of the .Net framework itself. My hunch is that part of the difficulty of putting the principle into practice when using the .Net framework is that many of us are not as familar as we should be with the wealth of interfaces (and abstract classes) the framework contains.

This article, the first in a 435 part series1, will catalog, explain, and provide examples of the key interfaces in the .Net framework2 in the hopes of bridging that gap and making it easier for us to put in practice what we know in theory. For the series premiere I decided not to cover an enigmatic, exotic, or universally applicable interface. Instead I chose two interfaces that will appeal to the heart of the working man: IDataReader and IDataRecord. These interfaces are not the sort to be fêted by technorati fancy lads or glamorized on the cover of MSDN magazine. They are more behind the scenes types, the sort you might see in your database abstraction layer or find in the signature of some data utility method. Used properly they can help promote reuse and further decouple your code from a specific database implementation. Intrigued? Read on...

IDataReader (and DbDataReader)

Description: IDataReader is part of the System.Data namespace. It provides an interface, both methods and properties, for reading one or more foward-only streams of data retrieved from a database. DbDataReader is an abstract class which inherits from MarshalByRefObject and implements IDataReader as well as IDisposible, IDataRecord, and IEnumerable. Like IDataReader, DbDataReader provides an interface for doing a foward-only read of records from a data source.

Most Important Properties/Methods: These are not all the properties or methods of the Interface, just the ones that I think are most important.

IsClosed: Gets a value indicating if the reader is closed.

Read: Advances the IDataReader to the next row (record). For example to advance the IDataReader to the first record you would type: reader.Read(). Read is most frequently used in a while loop.

NextResult: Advances the IDataReader to the next result set. A single IDataReader can hold multiple results set from a batched query. Batch queries like this can dramatically speed up database calls. For example, from the Northwind database imagine you want to return all records from the Customers, Employees, Suppliers, and Shippers table. This can be batched as a single query and each of the result set (tables) can be iterated through in turn.

Click to View the Example

Classes that Implement It: IDataReader is implemented by SqlDataReader and by the abstract class DbDataReader. DbDataReader is in turn implemented by OdbcDataReader, OracleDataReader, and all other XXXDataReader classes.

How to Use It: The chief use of IDataReader and DbDataReader is to decouple your code from the particular XXXDataReader you are actually using. For example, rather than the following:

SqlCommand command = new SqlCommand(queryString, connection); connection.Open(); SqlDataReader reader = command.ExecuteReader();

You would now code:

... IDataReader reader = command.ExecuteReader();

In fact, if you use Microsoft's Data Access Application Block it is an instance of IDataReader that gets returned from the ExecuteReader method.

Why is this important? Well, the main reason is that when you use an instance of IDataReader your code embodies the very principle stated at the begining of this article: Program to the interface, not the implementation. It promotes reuse by decoupling your code from a specific implementation. For example, a method like the one below can be used regardless of whether my database was Oracle, MYSQL, or SQL Server.

private SomeDomainObject MapObject(IDataReader reader) { SomeDomainObject o = new SomeDomainObject(); //In the section on IDataRecord, I will show a better //way to get the fields from a reader. For now this will suffice. //For clarity, I assume in the method the data reader gets closed elsewhere o.SomeField = reader[0]; o.SomeOtherField = reader[1]; return o; }

Another important reason, and this goes beyond the use of just IDataReader, is that your code expresses your intent. If you intend for a method to rely on a specific implementation then your method should reflect that. But if the contract is more general --for example, it does not matter that it is a SqlDataReader, only that it is some data reader then your method should reflect that too! Always expose in your methods the interface of the type that is the most general in that object's hierarchy to solve the problem you are trying to solve. Exposing IDataReader and not SqlDataReader in the above example illustrates this.

IDataRecord

Description: IDataRecord provides access to the column values for each row that was retrieved using a DataReader. An instance of IDataRecord is not created explicitly, but obtained as part of a DataReader.

Classes that Implement It: The most important class that implements it is DbDataReader as that class is the base class for all the XXXDataReaders. Other classes in the framework that implement IDataRecord are DbDataRecord, SqlDataRecord, SqlDataReader, and IDataReader.

Most Important Properties/Methods: As with IDataReader, these are not all the properties or methods of the Interface, just the ones that I think are most important.

Item: Item is an indexed property. It provides an easy way to get at the field values for the current row. For instance, reader[0] or reader["theField"]. Item returns an Object so the returned value will likely need to be cast to the desired type.

GetXXX: Like Item, the GetXXX methods --e.g., GetBoolean, GetString, etc.-- give you access to the requested field for the current row. Unlike Item, each GetXXX method returns the type appropriate for it.

IsDBNull: Just like it says, this method checks to see if the field is set to null --IsDBNull(0)

How to Use It: When you retrieve a row of data from a data reader and access its values, you are using the IDataRecord interface. Likewise when you have a DataReader dr and you write string s = dr.GetString(0), that method, and the other GetXXX methods, are part of the IDataRecord interface. You use this interface all the time when working with data readers, even if you are not always aware that you are doing so.

One of the best uses of IDataRecord is Shawn Wildermuth's Field class from his excellent, but now dated, book Pragmatic ADO.Net. The Field class is a collection of static methods that provide a simple way to determine if a field is null and if so, provide acceptable default values for those fields. Its use is illustrated below:

Rather than writing the following when getting values back from a data reader

if (r.IsDBNull(r[0]) == false) someString = r.GetString(0);

With the Field class you would access the columns via the appropriate static method corresponding to the underlying field type. If the column contained a database null value for that field, a type appropriate default value would be returned instead --e.g., an empty string for strings, zero for ints, etc. For example,

someString = Field.GetString(r, 0);

or, because it uses the field name and not the index, the even more readable

someString = Field.GetString(r, "theField");

I've included a modified version of the Field class below --which can downloaded by clicking the "Download the Code" button at the top of the page. Because of the changes I've made, any fault with the code surely lies with me and not its original author. Given the topic at hand you should note the extensive use of IDataRecord in the method signatures. Also, I know that you can do something similar with nullable types using the GetValueOrDefault method, but depending on your domain model this may not be the best approach.

Click to View the Code

Conclusion

Finally a few related interfaces that are germane to this topic, but which I am unlikely to catalog are: IDataAdapter, IDataParameter, IDbCommand, IDbConnection, and IDbTransaction. You should check them out if you are so inclined.

As this is the first article in what I hope will be an ongoing series, I would welcome any comments, critism, advice that you think would make future installments more useful. (Give me your worst!!!) I am planning on covering some of the new Generic Interfaces next time so stay tuned.

Toggle Footnotes Display

Comments

Subject Name Date Submitted
Feedback
Jason Gibson9/18/2006 10:30:30 AM
Excellent
Deelip9/25/2006 9:44:03 PM
Field Class
Larry10/21/2006 8:07:31 AM
re: Field Class
Chad Finsterwald10/21/2006 5:21:40 PM
Kill 2 birds with one stone
Steve10/23/2006 9:46:47 AM
re: Kill 2 birds with one stone
Chad Finsterwald10/24/2006 12:39:28 AM
IDataReader vs DbDataReader
Jon11/1/2006 4:34:17 PM
New Comment
(Your email address will not be displayed or shared.)
Please enter the code shown below. If you cannot read it, press "reset image" to generate a new one.