Menu

Mejorando nuestro acceso a datos 1/2

marzo 3, 2013 - .NET

En nuestra tarea de desarrollo de software empresarial día a día nos encontramos con la innegable necesidad de acceder a la base da datos, ya sea para extraer información o para guardarla, pero sabemos realmente cual es el método mas eficiente para la consulta de información en la base de datos. Si bien este articulo puede ser un poco anticuado pretende dar luces a quienes aun no usan EntityFramework o quienes comienzan.

Resultado

Proyecto

Acceso a datos

Requerimientos

Manos a la obra

Ok, hora de ensuciarnos las manos con Visual Studio, comenzaremos con algo que parece ser anticuado pero créanme que he visto aplicaciones utilizando aun este modelo de datos, comenzaremos con los DataTables.

Para todos los ejemplos utilizare la misma consulta:


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

Acceso a datos con DataTable

Si bien son algo anticuado aun hay gente que los usa mucho y por lo menos yo cuando los comencé a utilizar utilizaba el método DataTable.Load para cargarlo, solía preguntarme “Y para que diantres quiero yo utilizar un DataAdapter para cargarlo, con ese método basta”, ERROR!!!, Como ya veremos existe una forma mucho mas eficiente de cargar los DataTable, 4 veces mas rápido!!!.

El codigo a utilizar es el siguiente:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;

namespace Acceso_a_datos
{
	class Program
	{
		static void Main(string[] args)
		{
			string ConnectionString = "Server=localhost;Database=AdventureWorks2012;Trusted_Connection=True;";
			string Query = "SELECT BusinessEntityID, Title, FirstName, LastName  FROM Person.Person";
			Stopwatch Chronometer = new Stopwatch();

			//Load data with DataTable.Load method
			Chronometer.Start();
			LoadFromDataTableWithLoad(ConnectionString, Query);
			Chronometer.Stop();

			//Show result
			Console.WriteLine(String.Format("Load data with DataTable.Load method: {0} Milliseconds", Chronometer.ElapsedMilliseconds.ToString()));

			//Restart the chronometer
			Chronometer.Reset();

			//Load data with SqlDataAdapter.Fill method
			Chronometer.Start();
			LoadFromDataTableWithSqlDataAdapter(ConnectionString, Query);
			Chronometer.Stop();

			//Show result
			Console.WriteLine(String.Format("Load data with SqlDataAdapter.Fill method: {0} Milliseconds", Chronometer.ElapsedMilliseconds.ToString()));

			Console.ReadKey();
		}

		static DataTable LoadFromDataTableWithLoad(string ConnectionString, string Query)
		{
			DataTable Persons = null;

			try
			{
				using (SqlConnection Connection = new SqlConnection(ConnectionString))
				{
					Connection.Open();
					SqlCommand Command = new SqlCommand();
					Command.CommandText = Query;
					Command.Connection = Connection;
					Command.CommandType = CommandType.Text;
					using (SqlDataReader DataReader = Command.ExecuteReader())
					{
						Persons = new DataTable();
						Persons.Load(DataReader);
					}
				}
			}
			catch
			{
			}
			return Persons;  }

		static DataTable LoadFromDataTableWithSqlDataAdapter(string ConnectionString, string Query)
		{
			DataTable Persons = null;
			try
			{
				using (SqlConnection Connection = new SqlConnection(ConnectionString))
				{
					Connection.Open();
					SqlCommand Command = new SqlCommand();
					Command.CommandText = Query;
					Command.Connection = Connection;
					Command.CommandType = CommandType.Text;
					SqlDataAdapter DataAdapter = new SqlDataAdapter(Command);
					Persons = new DataTable();
					DataAdapter.Fill(Persons);
				}
			}
			catch
			{
			}
			return Persons;
		}
	}
}

Como ven nada del otro mundo, solo dos métodos para mostrar cuanto demora cada una de las técnicas de cargado, y en el main el Stopwatch que nos contabiliza el tiempo.

Los resultados de la ejecución:

Es una diferencia abismal, toma muchísimo menos tiempo utilizar un SqlDataAdapter para cargar los datos en el DataTable.

Ahora creo que es indiscutible que la técnica mas rápida de cargue de datos es con el DataReader y si utilizamos un escenario real, nuestra capa de acceso a datos muy posiblemente utilizaría Un DataReader con un objeto de negocio que inicialice cada una de sus propiedades, será mejor hacer esto o usar un DataTable?, Comparemos!

Creamos una clase Person:

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; }
	}
}

y este es el main:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;

namespace Acceso_a_datos
{
	class Program
	{
		static void Main(string[] args)
		{
			string ConnectionString = "Server=localhost;Database=AdventureWorks2012;Trusted_Connection=True;";
			string Query = "SELECT BusinessEntityID, Title, FirstName, LastName  FROM Person.Person";
			Stopwatch Chronometer = new Stopwatch();

			//Load data with DataTable.Load method
			Chronometer.Start();
			LoadFromDataTableWithLoad(ConnectionString, Query);
			Chronometer.Stop();

			//Show result
			Console.WriteLine(String.Format("Load data with DataTable.Load method: {0} Milliseconds", Chronometer.ElapsedMilliseconds.ToString()));

			//Restart the chronometer
			Chronometer.Reset();

			//Load data with SqlDataAdapter.Fill method
			Chronometer.Start();
			LoadFromDataTableWithSqlDataAdapter(ConnectionString, Query);
			Chronometer.Stop();

			//Show result
			Console.WriteLine(String.Format("Load data with SqlDataAdapter.Fill method: {0} Milliseconds", Chronometer.ElapsedMilliseconds.ToString()));

			//Restart the chronometer
			Chronometer.Reset();

			//Load data with LoadBusinessObject
			Chronometer.Start();
			LoadBusinessObject(ConnectionString, Query);
			Chronometer.Stop();

			//Show result
			Console.WriteLine(String.Format("Load data with LoadBusinessObject {0} Milliseconds", Chronometer.ElapsedMilliseconds.ToString()));

			Console.ReadKey();
		}

		static DataTable LoadFromDataTableWithLoad(string ConnectionString, string Query)
		{
			DataTable Persons = null;

			try
			{
				using (SqlConnection Connection = new SqlConnection(ConnectionString))
				{
					Connection.Open();
					SqlCommand Command = new SqlCommand();
					Command.CommandText = Query;
					Command.Connection = Connection;
					Command.CommandType = CommandType.Text;
					using (SqlDataReader DataReader = Command.ExecuteReader())
					{
						Persons = new DataTable();
						Persons.Load(DataReader);
					}
				}
			}
			catch
			{
			}
			return Persons;  }

		static DataTable LoadFromDataTableWithSqlDataAdapter(string ConnectionString, string Query)
		{
			DataTable Persons = null;
			try
			{
				using (SqlConnection Connection = new SqlConnection(ConnectionString))
				{
					Connection.Open();
					SqlCommand Command = new SqlCommand();
					Command.CommandText = Query;
					Command.Connection = Connection;
					Command.CommandType = CommandType.Text;
					SqlDataAdapter DataAdapter = new SqlDataAdapter(Command);
					Persons = new DataTable();
					DataAdapter.Fill(Persons);
				}
			}
			catch
			{
			}
			return Persons;
		}

		static List<Person> LoadBusinessObject(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())
					{
						while (DataReader.Read())
						{
							Person PersonItem = new Person();
							PersonItem.BusinessEntityID = Convert.ToInt32(DataReader["BusinessEntityID"]);
							PersonItem.FirstName = Convert.ToString(DataReader["FirstName"]);
							PersonItem.LastName = Convert.ToString(DataReader["LastName"]);
							PersonItem.Title = Convert.ToString(DataReader["Title"]);
							Persons.Add(PersonItem);
						}
					}
				}
			}
			catch { }
			return Persons;
		}
	}
}

Nuevamente nada del otro mundo, solo un while para iterar sobre el DataReader y la asignación de valores a cada una de las propiedades del objeto Person. Los resultados?:

Wow!, vamos mejorando nuestro tiempo de extracción de datos en la BD, solo utilizando ADO.NET, en esta ocasión vemos una mejora de un 88%!!

Conclusión

El manejo correcto del acceso a datos nos ayuda a tener tiempos bastante rápido en la extracción de los datos, esto obviamente ayuda a nuestra aplicación a ser mucho mas eficiente. Espero que les haya sido de ayuda esta entrega, en la próxima veremos como mejorar aun mas el tiempo utilizando objetos de negocio.

Deja un comentario

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