This question comes up occasionally, but I haven't seen a satisfactory answer.
A typical pattern is (row is a DataRow):
if (row["value"] != DBNull.Value)
{
someObject.Member = row["value"];
}
My first question is which is more efficient (I've flipped the condition):
row["value"] == DBNull.Value; // Or
row["value"] is DBNull; // Or
row["value"].GetType() == typeof(DBNull) // Or... any suggestions?
This indicates that .GetType() should be faster, but maybe the compiler knows a few tricks I don't?
Second question, is it worth caching the value of row["value"] or does the compiler optimize the indexer away anyway?
For example:
object valueHolder;
if (DBNull.Value == (valueHolder = row["value"])) {}
Notes:
I benchmarked a few scenarios (time in seconds, 10,000,000 trials):
row["value"] == DBNull.Value: 00:00:01.5478995
row["value"] is DBNull: 00:00:01.6306578
row["value"].GetType() == typeof(DBNull): 00:00:02.0138757
Object.ReferenceEquals has the same performance as "=="
The most interesting result? If you mismatch the name of the column by case (for example, "Value" instead of "value", it takes roughly ten times longer (for a string):
row["Value"] == DBNull.Value: 00:00:12.2792374
The moral of the story seems to be that if you can't look up a column by its index, then ensure that the column name you feed to the indexer matches the DataColumn's name exactly.
Caching the value also appears to be nearly twice as fast:
No Caching: 00:00:03.0996622
With Caching: 00:00:01.5659920
So the most efficient method seems to be:
object temp;
string variable;
if (DBNull.Value != (temp = row["value"]))
{
variable = temp.ToString();
}
I must be missing something. Isn't checking for DBNull
exactly what the DataRow.IsNull
method does?
I've been using the following two extension methods:
public static T? GetValue<T>(this DataRow row, string columnName) where T : struct
{
if (row.IsNull(columnName))
return null;
return row[columnName] as T?;
}
public static string GetText(this DataRow row, string columnName)
{
if (row.IsNull(columnName))
return string.Empty;
return row[columnName] as string ?? string.Empty;
}
Usage:
int? id = row.GetValue<int>("Id");
string name = row.GetText("Name");
double? price = row.GetValue<double>("Price");
If you didn't want Nullable<T>
return values for GetValue<T>
, you could easily return default(T)
or some other option instead.
On an unrelated note, here's a VB.NET alternative to Stevo3000's suggestion:
oSomeObject.IntMember = If(TryConvert(Of Integer)(oRow("Value")), iDefault)
oSomeObject.StringMember = If(TryCast(oRow("Name"), String), sDefault)
Function TryConvert(Of T As Structure)(ByVal obj As Object) As T?
If TypeOf obj Is T Then
Return New T?(DirectCast(obj, T))
Else
Return Nothing
End If
End Function