Using the SqlDataReader
class, what, if any, are the functional differences between:
(string) dataReader[\"MyFieldName\"];
and
//Well, we want to avoid the null exception issue entirely.
//Let's check for null first, before we try to use the value.
if( !dataReader.IsDBNull(dataReader.GetOrdinal("MyFieldName")))
{
//Store my data or use the value
string mystring=dataReader.GetString(dataReader.GetOrdinal("MyFieldName"));
}
Serendipity is a wonderful discovery method.
As an alternative to the answer from casperOne it would be easy to reduce ordinal lookups to occur once per the lifetime of the application. It can also be done without needing to manually maintain variables to record each of the indexes.
The following code expects a single query per class, if you want to handle multiple in one class it would be easy enough to achieve.
Start with a field:
static readonly ConcurrentDictionary<string, int> OrdinalMap =
new ConcurrentDictionary<string, int>();
Then update your access code similar to:
reader.GetString(OrdinalMap.GetOrAdd("MyFieldName", reader.GetOrdinal))
Now you have a threadsafe O(1) lookup for ordinals without needing to maintain any manual maps of variables or const usages that if you change the query break the world. Not that it should matter but just for clarity due to the behavior of GetOrAdd if you execute many queries concurrently it's possible that reader.GetOrdinal("MyFieldName")
may get executed several times, not exactly once but for all intents and purposes it could be viewed as once.
When dealing with a null value:
// Will throw an InvalidCastException
// Exception Message will be "Unable to cast object of type System.DBNull
// to System.String
(string) dataReader["MyFieldName"];
// Will throw a SqlNullValueException
// Exception Message will be "Data is Null. This method or property
// cannot be called on Null values."
dataReader.GetString(dataReader.GetOrdinal("MyFieldName"));
Casting issues aside, for the singular call, there are none. The indexer will make a call to DbDataReader.GetOrdinal and then call the appropriate Get
method to get the value (note that it's faster to call the Get
methods using an ordinal than it is to use the indexer with the field name).
However, this will incur a lookup of the ordinal every time. If you are iterating through a number of records in a forward-only, read-only way (which is exactly what DbDataReader instances are meant to do), then you can reduce the overhead of this lookup by doing it just once.
You could do so like this:
// Move to the first record. If no records, get out.
if (!dataReader.Read()) return;
// Before the loop. Can do this for any other fields being
// accessed in the loop as well.
int myFieldNameOrdinal = dataReader.GetOrdinal("MyFieldName");
// Process the records. Remember, already on the first record, so
// use do/while here.
do
{
// Do something with your field.
Console.WriteLine(dataReader.GetString(myFieldNameOrdinal));
} while (dataReader.Read());
In the first case you are casting which is ugly especially for value types (unboxing involved). Personally I always use the second and is what I would recommend you.