Sei sulla pagina 1di 8

C# ADO.

NET - Parte 1 - Recuperar Informacin Sql Server

Introduccin He notado que a veces faltan ejemplo integradores sobre el uso de ciertos aspectos que a primera vista parecen simple, pero al no encontrase en uso de forma integradora pueden resultar algo difciles de captar las ideas, especialmente cuando se comienza en el aprendizaje. La objetivo es mostrar cmo hacer que ado.net paso a paso en un ejemplo concreto utilizando varios mtodos de consultas y trabajo de la informacin obtenida Algunas aclaraciones antes de comenzar Si bien la explicacin del ejemplo se centrara en el uso de ado.net bajo lenguaje c#, los ejemplos podrn descargarse tambin en vb.net Las explicacin del artculo aplica a ambos lenguajes sin problemas. Con respecto al diseo del ejemplo por ah algunas aclaraciones previas podr facilitar la comprensin. Se visualizara que en el ejemplo se han declarado dos clases que por ah no son familiares, estas llevan el nombre: ContactoDAL y ContactoEntity. Para el que no conozca el DAL (Data Access Layer) en realidad apunta a separa en capas la aplicacin para dividir las responsabilidades y poder as encapsular y abstraer funcionalidad facilitando la reutilizacin. En este caso en particular aplique este mismo concepto pero no lo puse en un proyecto nuevo, simplemente para no complicar el ejemplo, es por eso que tanto estas clases como el formulario estn en el mismo proyecto. Tambin vern una clase que termina con la palabra entity, esta bsicamente representa la entidad del dominio, y define las propiedades que posee la tabla de contactos, esta entidad ser la intermediaria entre la funcionalidad de la DAL y la presentacin. Esta clase evitara que a la presentacin le lleguen objetos que claramente son de datos. Aunque vale aclarar que en este ejemplo al estar contenido en un solo proyecto se pierde un poco el objetivo de abstraer a la presentacin de componente de datos, pero igualmente esta es una buen tcnica para mantener el cdigo prolijo. 1 - Recuperar un conjunto de datos (DataReader) La primera operacin que ser utilizada ser la de recuperar todos los tems de una tabla y cargar una grilla con ellos. Para esta operacin hay dos formas para acceder a los datos, por medio de:

DataReader DataSet

Para los DataReader es que se hace uso de la clase con sufijo entity de esta forma se cargan los datos en estas entidades evitando pasar el reader a la presentacin, lo cual no es nada aconsejable. Algo importante para contar acerca de los reader es que estos necesitan mantener la conexin a al db abierta durante el procesamiento (o lectura de los registros que se estn recuperando), es por eso que usar clases de entidades ya que se procesa el reader en un tiempo muy corto y luego se cierra. Hay que destacar que lo reader en acceso secuencial de lectura son mas rpido que los dataset. Para el dataset, se hace uso de un DataSet tipado, en el proyecto lleva el nombre de dtoContacto, este permite tener una estructura definida, similar a la que brinda ContactoEntity. En la clase ContactoDAL, se encuentran dos mtodos GetAll y GetAllFromDataSet. Empezaremos explicando como hacer uso de DataReader.
01.public static List<ContactoEntity> GetAll() 02.{ 03.string sql = @"SELECT [IdContacto] 04.,[Nombre] 05.,[Apellido] 06.,[FechaNacimiento] 07.,[Localidad] 08.,[Calle] 09.,[Numero] 10.FROM Contacto"; 11. 12.List<ContactoEntity> list = new List<ContactoEntity>(); 13. 14.using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToStri ng())) 15.{ 16. 17.SqlCommand command = new SqlCommand(sql, conn); 18. 19.conn.Open(); 20. 21.SqlDataReader reader = command.ExecuteReader(); 22. 23.while (reader.Read()) 24.{ 25.list.Add(LoadContacto(reader)); 26.} 27. 28.return list; 29.} 30.

31.}

> Creacin del objeto de conexin a la base de datos (lnea 14): Este siempre ser el primer paso ya que es necesario definir el objeto que permitir establecer el contacto con la base de datos. Para realizar la tarea se necesitara de un string de conexin, para mas detalles ver Cadena de conexin. Algo interesante que observaran es el uso de la sentencia using, el objetivo de esta es permitir delimitar la seccin en que estar disponible un objeto, o sea el mbito en el cual la instancia ser usada, vern como al final cuando se termina de utilizar los objetos no se realiza un cierre de la conexin o un Dispose de los objetos, es justamente el bloque using el que har esto por nosotros. > Objeto Command (lnea 17): este objeto permitir unificar la consulta sql de seleccin que vamos a utilizar con la conexin a la base de datos que establecimos en el paso anterior. El objeto command posee la funcionalidad para ejecutar la consulta que se ha creado, ya sea esta para insertar o actualizar la informacin, o como en este caso para recuperarla. > Ejecucin de la consulta (lnea 21): el objeto command posee un mtodo de nombre ExecuteReader(), el cual devolver como resultado un objeto del tipo SqlDataReader. > Lectura de los datos devueltos (lneas 23-26): al tratarse de un grupo de registros los que se recuperaran de la consulta es necesario realizar un ciclo por cada uno de ellos realizando la transformacin de los datos para adecuarlos al objeto definido como entidad de negocio de la aplicacin, en este caso ContactoEntity El mtodo Read() del objeto SqlDataReader tiene dos funcionalidades bsicas, devuelve un true o false segn se encuentren registros para leer, y adems posiciona el cursor el el siguiente registro habilitado, es por eso que el while cumple la funcin ideal para recorrer cada tem del objeto DataReader. > Transformacin de los datos del reader a la entidad (lnea 25): esta operacin se ha encapsulado en un mtodo separado ya que es una operacin que ser reutilizada:
01.private static ContactoEntity LoadContacto(IDataReader reader) 02.{ 03.ContactoEntity contacto = new ContactoEntity(); 04. 05.contacto.IdContacto = Convert.ToInt32(reader["IdContacto"]); 06. 07.contacto.Nombre = Convert.ToString(reader["Nombre"]); 08.contacto.Apellido = Convert.ToString(reader["Apellido"]); 09. 10.contacto.FechaNacimiento = Convert.ToDateTime(reader["FechaNacimiento"]); 11. 12.contacto.Localidad = Convert.ToString(reader["Localidad"]); 13.contacto.Calle = Convert.ToString(reader["Calle"]); 14.contacto.Numero = Convert.ToInt16(reader["Numero"]); 15.

16. 17.return contacto; 18.}

Como se observara en el bloque de cdigo este mtodo crea una instancia nueva de la entidad que estamos utilizando, luego asigna a cada propiedad la columna que le corresponde del registro que se esta leyendo en ese momento, el cual se ha pasado como parmetro al mtodo, y por ultimo retorna el objeto entity con los datos para ser asignado a la coleccin de ContactoEntity. 2- Recuperar un conjunto de datos (DataSet) Esta operacin tendr mucho en comn con la anterior solo diferir en la utilizacin algunos objetos distintos ya que se har uso de un DataSet, (en este caso tipado) para cargar los datos

01.public static dtoContacto GetAllFromDataSet() 02.{ 03.string sql = @"SELECT [IdContacto] 04.,[Nombre] 05.,[Apellido] 06.,[FechaNacimiento] 07.,[Localidad] 08.,[Calle] 09.,[Numero] 10.FROM Contacto"; 11. 12.using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToStri ng())) 13.{ 14. 15.SqlCommand command = new SqlCommand(sql, conn); 16. 17.SqlDataAdapter da = new SqlDataAdapter(command); 18. 19.dtoContacto contactos = new dtoContacto(); 20. 21.da.Fill(contactos, "Contacto"); 22. 23.return contactos; 24.} 25. 26.}

Como se observara en el cdigo los dos primeros pasos son coincidentes con la operacin anterior que utilizaba DataReader para la obtencin de los datos, se detallara a continuacin las diferencias. > Creacin del adaptador (lnea 17): Este objeto DataAdapter proporcionara la herramienta principal para poder cargar un objeto del tipo DataSet.

Como ser observara el constructor del objeto SqlDataReader en este caso recibe al objeto Command como parmetro, pero debe comentarse que no es la nica alternativa, en este caso se uso el command para mantener una uniformidad en la codificacin, pero podra haber pasado como parmetro la consulta sql y el objeto conexin directamente al constructor del DataAdapter, de esta forma ya no se necesitara mas del objeto Command. > Creacin y llenado de los datos (lneas 19-21): La definicin del dataset tipado se encuentra en el archivo de nombre dtoContacto.xsd, al inspeccionar el archivo se notara que este contiene un DataTable de nombre Contacto y la definicin de las columnas, las cuales coinciden con las de la tabla de la base de datos. Nota: cuando se crea un DataSet Tipado lo mas comn es que se agregue de forma automtica un TablaAdapter, en este caso se removi dejando la entidad lo mas simple posible. El mtodo Fill() del DataAdapter realizara la ejecucin y carga de los datos en el DataSet Tipado, al cual adems le indicamos cual es el DataTable que se estar cargando. Debe recordarse que un DataSet puede contener mas de un DataTable, en este caso posee solo uno, pero podra haber mas en otros casos. Nota: al diferencia del objetos Command el SqlAdapter no requiere la apertura previa de la conexin, es por eso que la lnea conn.Open() no esta presente. El DataDapter realiza la apertura, utilizacin y cierre de la conexin de forma automtica 3 Recuperar un solo registro (DataReader) A diferencia la la operacin en donde recuperbamos todo la informacin de una tabla, en este caso se remarcan dos puntos - la utilizacin de parmetros en la consulta - la no utilizacin del while para recorrer los datos provenientes de la consulta

01.public static ContactoEntity GetById(int Id) 02.{ 03.string sql = @"SELECT [IdContacto] 04.,[Nombre] 05.,[Apellido] 06.,[FechaNacimiento] 07.,[Localidad] 08.,[Calle] 09.,[Numero] 10.FROM Contacto 11.WHERE IdContacto = @Id"; 12. 13.ContactoEntity product = null; 14. 15.using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToStri ng()))

16.{ 17. 18.SqlCommand command = new SqlCommand(sql, conn); 19.command.Parameters.AddWithValue("Id", Id); 20. 21.conn.Open(); 22. 23.SqlDataReader reader = command.ExecuteReader(); 24. 25.if (reader.Read()) 26.{ 27.product = LoadContacto(reader); 28.} 29. 30.return product; 31.} 32.}

> Creacin del parmetro de la consulta (lnea 19): como se observa el objeto Command posee una propiedad que permite definir los parmetros en la consulta, es all donde sern agregados, y hay diferentes formas de hacerlo, la mas practica y directa para crear parmetros simples es utilizando el mtodo AddWithValue(). Pero debe remarcarse un punto importante en el uso de este mtodo, como se observara en ningn momento se define un tipo de dato para el parmetro, lo cual parecera hasta mgico como lo infiere, pero no es as, para que esto funcione correctamente el tipo de la variable que se utiliza debe ser del tipo correcto, en este caso el Id es del tipo int, el cual coincide con el tipo de la columna Id de la tabla en la base de datos, es por esto que todo es resulto fcilmente. Por ejemplo, si el parmetro ser una fecha la variable usada en el mtodo AddWithValue, debera ser tambin del tipo fecha, o en su defecto se tendra que convertir (castear) al tipo DataTime. En definitiva el mtodo para agregar parmetros es simple de usar pero requiere ciertos cuidados a tener en cuenta, ya que de otra forma no podr determinar de forma dinmica el tipo de datos del parmetro. > Lectura del registro (lnea 25-28): a diferencia del proceso en donde se lean un grupo de registros, en este solo nos interesa uno solo, es por eso que al reemplazar el while por un simple if, este cumple la misma funcionalidad, el Read() del DataReader devuelve true si hay registro, y adems posiciona el cursor en el mismo para su lectura. Luego se reutiliza el mtodo que devolver la entidad completa, y esta es retornada como resultado del mtodo. 4 Recuperar un solo registro (DataSet) A diferencia el mtodo en donde se recuperaba un conjunto de registros aqu hay variantes - se utiliza un parmetro en la consulta - se devuelve un solo registro del DataTable y no el DataSet completo

01.public static dtoContacto.ContactoRow GetByIdFromDataSet(int Id) 02.{ 03.string sql = @"SELECT [IdContacto] 04.,[Nombre] 05.,[Apellido] 06.,[FechaNacimiento] 07.,[Localidad] 08.,[Calle] 09.,[Numero] 10.FROM Contacto 11.WHERE IdContacto = @Id"; 12. 13.using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToStri ng())) 14.{ 15. 16.SqlCommand command = new SqlCommand(sql, conn); 17. 18.SqlParameter param = new SqlParameter("Id", SqlDbType.Int); 19.param.Value = Id; 20.command.Parameters.Add(param); 21. 22.SqlDataAdapter da = new SqlDataAdapter(command); 23. 24.dtoContacto contactos = new dtoContacto(); 25. 26.da.Fill(contactos, "Contacto"); 27. 28.if (contactos.Contacto.Rows.Count > 0) 29.return contactos.Contacto.Rows[0] as dtoContacto.ContactoRow; 30.else 31.return null; 32.} 33.}

> Creacin del parmetro de la consulta (lnea 18-20): a modo de ejemplo en este caso se ha utilizado una tcnica distinta, en donde se define explcitamente cual es el tipo de datos del parmetro. Se crea el objeto SqlParameter, pasando en el constructor el nombre y tipo de dato del parmetro, luego a la instancia se le asigna el valor, y por ultimo se agrega a la coleccin de parmetros del objeto Command > Determinar si se encontr el registro, y retorno del ContactRow (lineas 28-31): Como en este caso lo que se carga es un Datset completo, en realidad un DataTable si se lo devuelve directamente quien deba manipular los datos deber agregar validaciones que comprueben si esta o no el registro cargado. Es por ello que estas validaciones se agrego del lado de los datos, en el if se pregunta si al menos hay un registro cargado, luego se toma el primero de la coleccin para retornarlo, en caso de no haber ninguno registro para los filtros asignados se devuelve null.

Al retornar el valor se castea a un tipo de dato algo particular dtoContacto.ContactoRow, esta es una clase que crea internamente el DataSet Tipado, y representa un registro del DataTable Contacto Cadena de Conexin Seguramente se habr notado en el cdigo que al hacer uso del objeto SqlConnection se utiliza una clase que devuelve la cadena de conexin:
ConfigurationManager.ConnectionStrings["default"]

El ConfigurationManager permite el acceso al archivo de configuracin situado en el app.config, este archivo al editarlo posee un formato xml en donde se podr especificar la configuracin del sistema, en este caso en particular se utiliza para especificar la cadena de conexin a la base de datos, pero podra servir para conservar otro tipo de datos variables para el sistema. Lo bueno de esta implementacin es que al compilar la aplicacin el archivo de configuracin queda libre como archivo de texto que podr editarse con el notepad y cambiarse sin necesidad de recompilar la aplicacin desarrollada. Esto es muy bueno para realizar cambios una vez que se ha realizado el deploy en la pc del usuario. Para utilizar el ConfigurationManager es necesario agregar la referencia a la assembly de nombre System.Configuration

Consideraciones acerca de la aplicacin de ejemplo Los ejemplos de cdigo estn usando Sql Server como parte de la solucin, por lo tanto se aconseja que localmente al menos se tenga instado el Sql Server 2008 Express con el servicio ejecutndose. Para editar un item de la grilla una vez que este cargada, se deber hacer click con el botn derecho del mouse sobre de la grilla, y seleccionar con que operacin recuperar los datos. Se esta haciendo uso control ContextMenuStrip para la edicin del registro seleccionado

Potrebbero piacerti anche