Obtener valores tipados de IDataReader

¿Qué es un IDataReader?

La documentación oficial de microsoft lo define como:

Provides a means of reading one or more forward-only streams of result sets obtained by executing a command at a data source, and is implemented by .NET data providers that access relational databases.

En Resumen, se trata de una interface que nos permite leer un conjunto de datos de manera secuencial hacia adelante, se implementa mediante proveedores de datos que tienen acceso a bases de datos relacionales.

Implementaciones

Al tratarse de una interfaz, esta puede tener diferentes implementaciones las cuales dependerán del proveedor de datos algunas de las mas populares son:

  • SqlDataReader
  • MySqlDataReader
  • SqliteDataReader

¿Cuál es el problema?

La interfaz expone diversos métodos para para acceder a los valores de los registros, sin embargo la manera de acceder es mediante la posición del campo, por ejemplo:

using var connection = new SqlConnection();
using var command = connection.CreateCommand();

command.CommandText = "StoredProcedureName";
command.CommandType = CommandType.StoredProcedure;

using IDataReader rd = command.ExecuteReader();
var person = new Person
{
    Name = rd.GetString(0),
    LastName = rd.GetString(1),
    DateOfBirth = rd.GetDateTime(2)
};

¿Cómo solucionarlo?

Podemos crear nuestro propio método que nos permita acceder a los valores mediante su nombre y asignando el tipo, como se ve en este ejemplo

using var connection = new SqlConnection();
using var command = connection.CreateCommand();

command.CommandText = "StoredProcedureName";
command.CommandType = CommandType.StoredProcedure;

using IDataReader rd = command.ExecuteReader();
var person = new Person
{
    Name = rd.GetField<string>("Name"),
    LastName = rd.GetField<string>("LastName"),
    DateOfBirth = rd.GetField<DateTime>("DateOfBirth")
};

¿Cómo funciona?

Haciendo uso de los métodos de extensión debemos crear la siguiente clase estática

public static class Extensions 
{
    public static T GetField<T>(this IDataReader reader, string name)
    {
        var value = reader[name];

                // En caso que el valor sea nulo retornara el valor default del tipo
        if (value == null || value == DBNull.Value)
        { return default(T); }

                // En caso que se trate convertir a un Enum, el metodo de coversion es diferente
        if (typeof(T).IsEnum)
        { return (T)Enum.Parse(typeof(T), value.ToString()); }

                // En caso que se trate convertir a un Nullable<>, el metodo de coversion es diferente
        if (Nullable.GetUnderlyingType(typeof(T)) != null)
        { return (T)value; }

                // Metodo de conversión default
        return (T)Convert.ChangeType(value, typeof(T));
    }
}

Pero la clase SqlDataReader ya cuenta con un método similar

Es correcto, la clase SqlDataReader ya cuenta con el método de extensión GetFieldValue sin embargo al utilizar esta clase perdemos la modularidad que obtenemos al usar IDataReader ya que al utilizar solo las interfaces podríamos por ejemplo cambiar de SQL Server a MySQL de una manera sencilla

24