martes, agosto 18, 2009

 

C# y Patrones de diseño


miércoles, mayo 09, 2007

 

Introduccion a ADO.NET

ADO.Net es la interface para escribir aplicaciones que interactuan con bases de datos. Esta compuesta por un conjunto de clases de la BCL del .NET Framework, las cuales vamos a estudiar una por una a detalle.

Vamos a empezar con el estudio de las clases basicas: Connection, Command y DataReader

Para conectar nuestra aplicacion con el origen de los datos (tipicamente un servidor de bases de datos) usaremos un ejemplar (objeto) de la clase Connection.

Los ejemplares de la clase Command nos van a permitir enviar comandos hacia el servidor para que este los ejecute.

Si la orden enviada al servidor es una consulta entoces vamos a requerir un ejemplar de la clase DataReader para recibir cada registro de datos que el servidor va e anviar como respuesta a la consulta.

Antes de continuar es necesario aclarar que las clases anteriores sirven para definir el comportamiento antes mencionado y no las utilizaremos directamente. En su lugar usaremos clases especificas para cada origen de datos con los que queramos interactuar. A estas clases especificas se les denomina "proveedores de datos". Actualmente estan disponible en Visual Studio los siguientes:





Estos son los que distribuye Microsoft, sin embargo se encuentran disponible algunos mas desarrollados por terceros, por ejemplo para el servidor FireBird entre otros.
Es importante repetir que las caracteristicas de todos ellos son similares.
Los ejemplos desde este punto en adelante los vamos a tratar con el proveedor de Sql Server (SqlConnection, SqlCommand, SqlDataReader y SqlDataAdapter) pero recordando siempre sera lo mismo para cualquiera de los otros.

Entonces, para conectarnos a un servidor SqlServer debemos crear un ejemplar de la clase SqlConnection, configurar la propiedad ConnectionString y ejecutar el metodo Open().

SqlConnection conx = new SqlConnection;
conx.ConnectionString = "Data Source = NombreServidor; Initial Catalog = BaseDatos; Integrated Security = Yes";
conx.Open();
...
conx.Close();


La propiedad ConnectionString, es una cadena de caracteres que define una seria de parametros separados por ";" y cada uno de estos debe tener un valor. Los parametros dependen del tipo servidor. Para el caso de Sql Server estos son varios pero obligatoriamente debemos poner los siguientes :

Tambien es necesario especificar nuestro nombre de usuario y contraseña, en el caso de SqlServer existen dos posibilidades:

Enviar nombre de usuario y contraseña definidos en el servidor mediante los siguientes parametros:

o bien, utilizar el nombre de usuario y contraseña con los que se inicio la sesion en Windows (seguridad integrada), mediante el siguiente parametro:
Despues de especificar la propiedad ConnectionString ya estamos en condiciones de iniciar o abrir la coneccion, esto se realiza llamando al metodo Open() del objeto SqlConnection. Cuando ya no vamos a trabajar con el servidor vamos a cerrar la coneccion llamando al metodo Close().

Despues de abrir la coneccion nuestro programa esta en condiciones de pedirle al servidor que ejecute alguna instruccion o procedimiento almacenado. Estas seran instrucciones en el lenguage SQL y se enviaran solo a traves de un ejemplar de la clase Command. Los pasos para enviar una instruccion son:

crear el ejemplar :

SlqCommand inst = new SqlCommand( )

establecer las propiedades Connection y CommandText :

inst.Connection = conx;
// conx es un ejemplar de SqlConnection
inst.CommandText = "Delete From Alumnos"; // aqui va la instruccion o el nombre del procedimiento almacenado


enviar la instruccion al servidor para que este la ejecute. Para esto se utilizara uno de los metodos execute del command :

inst.ExecuteNonQuery(); // Si la instruccion o procedimiento no es una consulta (Select ... )
inst.ExecuteScalar();
// Si la instruccion o procedimiento es una consulta (Select ... ) y solo regresa un valor
inst.ExecuteReader();
// Si la instruccion o procedimiento es una consulta (Select ... )

En nuestro ejemplo la instruccion es "Delete From Alumnos" por lo tanto se enviara al servidor de esta forma:

inst.ExecuteNonQuery();


Ademas ExecuteNonQuery() retorna un entero que indica la cantidad de registros afectados por la instruccion enviada.

Si la instruccion fuera una consulta que regresa solo un valor, por ejemplo "Select Count(*) From Alumnos" la instruccion adecuada es

int alumno = (int) inst.ExecuteScalar();


El metodo ExecuteScalar() retorna un valor de tipo Object por lo que es necesario hacer una conversion (cast) al tipo de dato correcto, en el ejemplo el tipo real que retorna es un entero.

Si la instruccion fuera una consulta que regresa un numero indeterminado de datos, por ejemplo "Select Matricula,Nombre From Alumnos" la instruccion adecuada es

SqlDataReader lector;
lector = inst.ExecuteReader();


En este caso se va a utilizar el metodo ExecuteReader(). Este metodo provoca las siguientes acciones:
  1. Envia la consulta el servidor.
  2. Crea un ejemplar de la clase SqlDataReader y lo prepara (abre ) para recuperar los datos provenientes de servidor.
  3. Regresa una referencia hacia el ejemplar creado.
Por lo tanto es nuestra obligacion almacenar la referencia al SqlDataReader en una variable para poder recuperar la informacion que envie el servidor. En el ejemplo anterior la variable utilizada se llama lector.

Una vez que tenemos el DataReader podemos empezar a recuperar cada registro de datos que envie el servidor ejecutando repetidamente el metodo Read() de este. La ejecucion del metodo Read() provoca que el servidor envie un registro de datos si es que hay alguno disponible.

En caso de que efectivamente se reciba un registro de datos, el DataReader lo almacena internamente y el metodo retorna un valor true, de lo contrario retorna un false.

Es muy importante tener en cuenta que el DataReader solo tiene capacidad de almacenar los datos el ultimo registro recuperado (cada nuevo registro sustituye los datos del anterior) por lo que es nuestra obligacion hacer
algo con los datos recibidos antes de llamar de nuevo al metodo Read(). Como ejemplo vamos mostrar en pantalla los datos recibidos en la consulta anterior:

SqlDataReader lector;
lector = inst.ExecuteReader();

while (lector.Read( ) ){
// recuperamos un registro de datos
mat = lector.GetString(0);
// obtenemos el valor de la primer columna
nom = lector.GetString(1);
//
obtenemos el valor de la segunda columna
Console.WriteLine("{0} {1}", mat, nom);
}
lector.Close(); // muy importante


Para obtener los datos almacenados en un DataReader disponemos de los metodos GetXXX ( GetString, GetInt32, GetBoolean, etc.), debemos utilizar el correcto segun el tipo de dato. Cada uno de estos recibe como argumento un entero que indica la posicion de la columna de datos deseada (la numeracion empieza en cero).

Si bien podemos tener cualquier cantidad de ejemplares DataReader al mismo tiempo, solo es posible tener abierto uno de ellos a la vez por lo que muy importante llamar al metodo Close() del DataReader una vez que ya no lo ocupamos. Si no lo hacemos seguramente tendremos problemas como el siguiente :

SqlDataReader lector1, lector2;
lector = inst1.ExecuteReader();

while (lector1.Read( ) ){

// hacer algo
}

lector = inst2.ExecuteReader(); // esta instruccion va a provocar una excepcion ( un fallo en tiempo de ejecucion)

while (lector2.Read( ) ){

// hacer algo
}

El error se va a dar en la siguiente linea

lector = inst2.ExecuteReader();


Anteriormente vimos que el metodo ExecuteReader() entre otras cosas crea y abre un SqlDataReader, entonces el error se provoca cuando este metodo intenta abrir el SqlDataReader recien creado porque existe otro SqlDataReader abierto.





This page is powered by Blogger. Isn't yours?

Suscribirse a Entradas [Atom]