I want to initialize a class with data coming from a MySql db. Some fields can be null:
Dim dr As MySqlDataReader = ...
Dim item As New Item(dr.GetInt16(0), dr.GetString(1), dr.GetString(2))
Suppose the last two fields could be NULL In the db, so that calling GetString on that field causes an exception.
I could certainly write code to test for NULLs before I get each field:
dim field1 as String
if ( dr.IsDbNull(1) )
field1 = Nothing ' or even ""
else
field1 = dr.GetString(1)
But if you have many fields this is an "ifs" nightmare.
To this purpose I rewrote the IIf VB function to make it more typed, thus to avoid casts:
Namespace Util
Public Shared Function IIf(Of T)(ByVal condition As Boolean, ByVal iftrue As T, ByVal iffalse As T) As T
If condition Then Return iftrue Else Return iffalse
End Function
So that I could write something like:
Dim item As New Item(
dr.GetInt16(0),
Util.IIf(dr.IsDbNull(1), "", dr.GetString(1),
Util.IIf(dr.IsDbNull(2), "", dr.GetString(2))
The typed IIf works well in other cases, but unfortunately it doesn't in this instance, because being it a normal function and not a language keyword, each inpout parameter is evaluated during the call, and when the field is NULL the exception is raised.
Can you think of an elegant if-less solution?
First off, I'd recommend you to use an ORM mapper - there are very few cases nowadays when you have to do manual "mapping".
If this is one of these cases, I'd recommend you to use field names instead of indexes while accessing Data Reader.
And to answer your original question: try extension methods. Sorry for C#, but VB.NET syntax drives me nuts:
public static class DbDataReaderExtensions
{
public static T GetField<T>(this DbDataReader dbDataReader, string fieldName,
T defaultValue)
{
if(dbDataReader.IsDBNull(fieldName))
return defaultValue;
return (T)dbDataReader[fieldName];
}
}