Menu

Mejorando nuestro acceso a datos 2/2

Marzo 12, 2013 - .NET

Introducción

En la entrada anterior habíamos visto diferentes técnicas para extracción de datos de la BD y qué tan eficientes son, en esta entrada tomaremos la última técnica utilizada (Cargue con DataReader a un objeto de negocio) y la optimizaremos aún mas.

Proyecto

Les dejo el proyecto de Acceso a datos.

Requerimientos

Manos a la obra

Para todos los ejemplos utilizare la misma consulta:


SELECT BusinessEntityID, Title, FirstName, LastName FROM Person.Person

y el código correspondiente a la clase person es el mismo que hemos estado utilizando:

namespace Acceso_a_datos
{
public class Person
{
public int BusinessEntityID { get; set; }
public string Title { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}

Habiendo dicho esto, dispongámonos a optimizar lo optimizado, ¿como?, si nos fijamos cuando vamos a extraer los datos en el DataReader, existe una sobrecarga que recibe un entero:

nos dice que obtiene el valor de una columna especifica dado el Column Ordinal y si revisamos la lista de métodos que tiene un objeto DataReader, efectivamente encontramos un método DataReader.GetOrdinal(“NombreColumna”), así pues podemos optimizar un poco más la extracción de datos pasando el Ordinal en vez del nombre de la columna ya que internamente lo que hace el compilador de .NET es obtener los ordinal de los nombres de la columna que le hayamos pasado, así que mejor le evitamos ese paso y así podemos optimizar aún mas nuestra aplicación:

static List<Person> LoadBusinessObjectWithOrdinals(string ConnectionString, string Query)
{
List<Person> Persons = new List<Person>();

try
{
using (SqlConnection Connection = new SqlConnection(ConnectionString))
{
SqlCommand Command = new SqlCommand();
Command.CommandText = Query;
Command.Connection = Connection;
Command.CommandType = CommandType.Text;
Connection.Open();

using (SqlDataReader DataReader = Command.ExecuteReader())
{
//Get ordinal
int OrdinalBusinessEntityID = DataReader.GetOrdinal("BusinessEntityID");
int OrdinalFirstName = DataReader.GetOrdinal("FirstName");
int OrdinalLastName = DataReader.GetOrdinal("LastName");
int OrdinalTitle = DataReader.GetOrdinal("Title");

while (DataReader.Read())
{
Person PersonItem = new Person();
PersonItem.BusinessEntityID = Convert.ToInt32(DataReader[OrdinalBusinessEntityID]);
PersonItem.FirstName = Convert.ToString(DataReader[OrdinalFirstName]);
PersonItem.LastName = Convert.ToString(DataReader[OrdinalLastName]);
PersonItem.Title = Convert.ToString(DataReader[OrdinalTitle]);
Persons.Add(PersonItem);
}
}
}
}
catch { }
return Persons;
}

Eso si, hay que evitar hacer el GetOrdinal dentro del ciclo, ya que no estaríamos haciendo nada. El resultado:

Puede que 5 milésimas de segundo no sea mucho pero si multiplican esto por la cantidad de usuarios que usen la aplicación a la final será mucho tiempo ahorrado.

Ahora, si vemos la imagen del intellisense dice: “Gets the value of the specified column in its native form”, en otras palabras estamos extrayendo la información del campo en su formato nativo y luego le aplicamos el convert, que tal si en vez de extraerla en su formato nativo, le decimos que la extraiga en el formato que deseamos?:

static List<Person> LoadBusinessObjectWithOrdinalsWithoutConvert(string ConnectionString, string Query)
{
List<Person> Persons = new List<Person>();

try
{
using (SqlConnection Connection = new SqlConnection(ConnectionString))
{
SqlCommand Command = new SqlCommand();
Command.CommandText = Query;
Command.Connection = Connection;
Command.CommandType = CommandType.Text;
Connection.Open();

using (SqlDataReader DataReader = Command.ExecuteReader())
{
//Get ordinal
int OrdinalBusinessEntityID = DataReader.GetOrdinal("BusinessEntityID");
int OrdinalFirstName = DataReader.GetOrdinal("FirstName");
int OrdinalLastName = DataReader.GetOrdinal("LastName");
int OrdinalTitle = DataReader.GetOrdinal("Title");

while (DataReader.Read())
{
Person PersonItem = new Person();
PersonItem.BusinessEntityID = DataReader.GetInt32(OrdinalBusinessEntityID);
PersonItem.FirstName = DataReader.GetString(OrdinalFirstName);
PersonItem.LastName = DataReader.GetString(OrdinalLastName);
PersonItem.Title = DataReader.GetString(OrdinalTitle);
Persons.Add(PersonItem);
}
}
}
}
catch { }
return Persons;
}

y por último, como plus les doy una última técnica, que entraría en conflicto con la que acabamos de ver, personalmente no la utilizo y es extraer los valores la fila de una vez y guardarlos en un array, así leemos el array con la información, en vez de leer del DataReader directamente:

static List<Person> LoadBusinessObjectWithOrdinalsGettingValues(string ConnectionString, string Query)
{
List<Person> Persons = new List<Person>();

try
{
using (SqlConnection Connection = new SqlConnection(ConnectionString))
{
SqlCommand Command = new SqlCommand();
Command.CommandText = Query;
Command.Connection = Connection;
Command.CommandType = CommandType.Text;
Connection.Open();

using (SqlDataReader DataReader = Command.ExecuteReader())
{
//Get ordinal
int OrdinalBusinessEntityID = DataReader.GetOrdinal("BusinessEntityID");
int OrdinalFirstName = DataReader.GetOrdinal("FirstName");
int OrdinalLastName = DataReader.GetOrdinal("LastName");
int OrdinalTitle = DataReader.GetOrdinal("Title");

int NoColumns = DataReader.FieldCount;
object[] Row = new object[NoColumns];

while (DataReader.Read())
{
Person PersonItem = new Person();
DataReader.GetValues(Row);

PersonItem.BusinessEntityID = Convert.ToInt32(Row[OrdinalBusinessEntityID]);
PersonItem.FirstName = Convert.ToString(Row[OrdinalFirstName]);
PersonItem.LastName = Convert.ToString(Row[OrdinalLastName]);
PersonItem.Title = Convert.ToString(Row[OrdinalTitle]);
Persons.Add(PersonItem);
}
}
}
}
catch { }
return Persons;
}

El resultado en mi opinión, no es concluyente, sin embargo debo anotar que siempre que lo ejecuto, el resultdo es siempre unas 2 o 3 milésimas de segundo mejor:

Conclusión

En nuestro viaje de optimización hemos visto como de 300 milésimas de segundo pasamos a unas sorprendentes 32 milésimas (Según el último ejemplo) utilizando técnicas de cargue de información propias del ADO.NET, éstas técnicas no son tan intrusivas y requieren un mínimo esfuerzo, las ventajas una aplicación mucho mas rápida.

Espero que les haya sido de utilidad y como siempre Happy Coding!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *