|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Introduction.NET 2.0 introduced nullable types into the CLR which, for the first time, provided the ability for value types to be assigned a null value. However, ADO.NET 2.0 did not introduce any new features specifically for dealing with nullable types. Therefore, one of the primary goals for the classes presented in this article is to provide a simple API for working with nullable types within the persistence layer. There are three primary goals that the data readers will address.
Goal #1Goal #1 is to provide a simple, unified, strongly-typed API for dealing with non-nullable types for both an
The person.Age = reader.GetInt32(reader.GetOrdinal("Age"));
It would be much more convenient to write the method like this: person.Age = reader.GetInt32("Age");
When working with a person.Age = (int)row["Age"];
or you have to using the person.Age = Convert.ToInt32(row["Age"]);
It would be more convenient if you could access data from the Goal #2Goal #2 is to provide a simple, strongly-typed API for dealing with nullable types. The if (reader.IsDBNull(reader.GetOrdinal("FiredDate")))
person.FiredDate = null;
else
person.FiredDate =
reader.GetDateTime(reader.GetOrdinal("FiredDate"));
This is clearly not ideal – it would be best to have the same strongly-typed Goal #3Goal #3 is to provide a unified interface so that the same code can be used polymorphically to consume either an The INullableReaderThe Interface definition: public interface INullableReader
{
bool GetBoolean(string name);
Nullable<bool> GetNullableBoolean(string name);
byte GetByte(string name);
Nullable<byte> GetNullableByte(string name);
char GetChar(string name);
Nullable<char> GetNullableChar(string name);
DateTime GetDateTime(string name);
Nullable<DateTime> GetNullableDateTime(string name);
decimal GetDecimal(string name);
Nullable<Decimal> GetNullableDecimal(string name);
double GetDouble(string name);
Nullable<double> GetNullableDouble(string name);
float GetFloat(string name);
Nullable<float> GetNullableFloat(string name);
Guid GetGuid(string name);
Nullable<Guid> GetNullableGuid(string name);
short GetInt16(string name);
Nullable<short> GetNullableInt16(string name);
int GetInt32(string name);
Nullable<int> GetNullableInt32(string name);
long GetInt64(string name);
Nullable<long> GetNullableInt64(string name);
string GetString(string name);
string GetNullableString(string name);
object GetValue(string name);
bool IsDBNull(string name);
}
Although this interface provides NullableDataReaderThe To instantiate a dr = new NullableDataReader(db.ExecuteReader(cmd));
Example with raw ADO.NET: dr = new NullableDataReader(cmd.ExecuteReader());
To read values, simply refer to the column names: person.Age = dr.GetInt32("Age");
person.FiredDate = dr.GetNullableDateTime("FiredDate");
The try
{
while (dr.Read())
{
Person person = new Person();
person.Age = dr.GetInt32("Age");
person.FiredDate =
dr.GetNullableDateTime("FiredDate");
personList.Add(person);
}
}
finally
{
dr.Dispose();
}
The above code looks no different than that of any other data reader except:
NullableDataRowReaderThe Instantiate a If reading a single row, then passing a NullableDataRowReader dr = new NullableDataRowReader(row);
person.Age = dr.GetInt32("Age");
person.FiredDate = dr.GetNullableDateTime("FiredDate");
Notice, the access methods look identical to that of the If reading multiple rows (e.g., while iterating through a loop), then assigning the NullableDataRowReader dr = new NullableDataRowReader();
foreach (DataRow row in dataTable.Rows)
{
dr.Row = row;
Person person = new Person();
person.Age = dr.GetInt32("Age");
person.FiredDate = dr.GetNullableDateTime("FiredDate");
personList.Add(person);
}
In the above example, we just iterated all the rows of a NullableDataReader dr = new
NullableDataReader(dataTable.CreateDataReader());
However, when working with Polymorphic NullableReaderIn some cases, we may need to populate a business object with a public Address BuildItem(INullableReader dr)
{
Address address = new Address();
address.ID = dr.GetInt32(Params.AddressID);
address.StreetAddress1 = dr.GetString(Params.StreetAddress1);
address.StreetAddress2 = dr.GetString(Params.StreetAddress2);
address.City = dr.GetString(Params.City);
address.State = dr.GetString(Params.State);
address.ZipCode = dr.GetString(Params.ZipCode);
return address;
}
The NullableDataReader dr = new
NullableDataReader(db.ExecuteReader(cmd));
person.Address = addressMapper.BuildItem(dr);
And secondly, with a NullableDataRowReader dr = new
NullableDataRowReader(addressTable.Rows[0]);
person.Address = addressMapper.BuildItem(dr);
Implementation DetailsInternally, the
Of course, to consume the data readers, the developer is not required to be aware of any of these implementation details. To illustrate the internal implementation, we will examine the public int GetInt32(int i)
{
return reader.GetInt32(i);
}
To provide an overloaded method that takes a column name instead of an ordinal, the standard approach is used while shielding the implementation from the consumer: public int GetInt32(string name)
{
return reader.GetInt32(reader.GetOrdinal(name));
}
Up to this point, we haven't done anything terribly interesting (although the new overload has provided considerable convenience). To provide two overloads to the public Nullable<int> GetNullableInt32(string name)
{
return this.GetNullableInt32(reader.GetOrdinal(name));
}
public Nullable<int> GetNullableInt32(int index)
{
Nullable<int> nullable;
if (reader.IsDBNull(index))
{
nullable = null;
}
else
{
nullable = GetInt32(index);
}
return nullable;
}
However, the problem here is that, while not complicated, the nullable assignment with the
To address this issue and produce code that is more concise and elegant, we can utilize a generic method that includes passing a delegate which we now have available as part of the C# 2.0 anonymous methods functionality. Therefore, we can simply create one method that performs the assignment: private Nullable<T> GetNullable<T>(int ordinal,
Conversion<T> convert) where T : struct
{
Nullable<T> nullable;
if (reader.IsDBNull(ordinal))
{
nullable = null;
}
else
{
nullable = convert(ordinal);
}
return nullable;
}
First, notice that we are using a generic type and specified the constraint that private delegate T Conversion<T>(int ordinal);
This makes it possible for all of the public Nullable
Notice that the second argument called is actually utilizing C# 2.0 delegate inference, and specifying that the normal Having the ability to utilize the same method for every
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||