Revista Informática

¿Como conectar SQL Server con C#?

Publicado el 04 julio 2014 por Jamesrevelo @herprogramacion
Conectar nuestras aplicaciones C# a una base de datos en SQL Server es uno de los requerimientos mas importantes de funcionalidad en nuestros proyectos. Por eso hoy aprenderemos una forma de realizar esta conexión y veremos ejemplos que nos guíen a través de todo el articulo.
En primera instancia veremos como abrir la conexión al servidor, luego veremos como ejecutar comandos , también aprenderemos a mostrar los datos de una consulta en un DataGridView y finalmente estudiarémos la ejecución de procedimientos almacenados.
Conexion Sql Server y C#

¿Cual es el primer paso?

Lo primero es crear una conexión de red entre la aplicación y el servidor de bases de datos. Para ello usaremos la clase SqlConnection del namespace SqlClient. Este espacio de nombres es el encargado de gestionar los datos en SQL Server hacia el Framework .NET. Es solo declarar un nuevo objeto de conexión similar la siguiente linea de código:
using System.Data.SqlClient;
...
SqlConnection con = newSqlConnection();
Para nuestro objeto con es de vital importancia los datos que abren la conexión, los cuales estarán almacenados en un atributo llamado ConnectionString de tipo string. Para establecerlo podemos usar el constructor o asignárselo luego de la inicialización. Veamos:
string datosConexion = "Data Source=localhost;"
+"Initial Catalog=facturacion;Integrated Security=true;";
ó
SqlConnectioncon = new SqlConnection();
con.ConnectionString = datosConexion;
La cadena de conexión indica las características necesarias para la conexión. Veamos la definición de algunas:
  • Data Source: Se refiere al origen de datos, nombre del servidor o dirección donde se encuentra la base de datos.
  • Initial Catalog: Es el nombre de la base de datos a la que deseamos acceder.
  • Integrated Security: Si usas true el ingreso a la base de datos se autentica con los permisos del usuarios actual de Windows, si usas false, debes indicar en la cadena el nombre de usuario(UID) y la constraseña(PWD).
Existen mas características, pero por ahora están fuera del alcance de este articulo. Puedes consultarlas luego por tu cuenta.
Luego de establecer la cadena de conexión procedemos a abrir la conexión hacia el servidor con el método Open():
con.Open();
Al realizar todas las operaciones sobre nuestra base de datos es importante cerrar la conexión con el método Close():
con.Close();
Otra alternativa para asegurar que la conexión se cierra es usando un bloque using en C#:
using (SqlConnection con = new SqlConnection(datoConexion))
{
con.Open();
//Bloque de instrucciones sobre la base de datos
}
Al finalizar dicho bloque la conexión se cierra inmediatamente, liberándonos de esta responsabilidad.

Excelente!, pero... ¿como ejecutamos un SELECT o un INSERT...o algo?

Buena pregunta!, para ejecutar comandos T-SQL usaremos la clase SqlCommand(hace parte de SqlClient). Solo debemos crear una instancia de esta clase y asociar una cadena que guarde el comando y el objeto que esta gestionando la conexión.
El atributo que guarda el texto del comando se llama CommandText y es de tipo string, lo usaremos en el constructor del objeto comando de la siguiente forma:
SqlCommand cmd = new SqlCommand(textoCmd, con);
El primer parámetro es CommandText y el segundo la conexion asociada. Miremos el siguiente ejemplo:
string textoCmd = "DELETE FROM CLIENTE WHERE IDCLIENTE = 1112;"
SqlCommandcmd = new SqlCommand(textoCmd,con);
Para ejecutarlo usamos el método ExecuteNonQuery(), que ejecuta sentencias que no retornan filas como INSERT, UPDATE, DELETE ó SET.
cmd.ExecuteNonQuery();

¿Como quedaría todo el código completo?

Añadamos cada paso en orden para completar un pequeño ejemplo:
static void Main(string[] args)
   {
   // Paso 1 - Crear una instancia de la clase SqlConnection
     stringdatosConexion = "Data Source = localhost;"
   +"Initial Catalog = CLIENTE ; Integrated Security = true;";
   try
   {
   using (SqlConnectioncon = new SqlConnection(datosConexion))
   {
   //Paso 2 - Abrir la conexión
   con.Open();
   // Paso 3 - Crear un nuevo comando
   stringtextoCmd = "DELETE FROM CLIENTE WHERE IDCLIENTE = 1112;";
   SqlCommand cmd = new SqlCommand(textoCmd,con);
   //Paso 4 - Ejecutar el comando
   try
   {
   cmd.ExecuteNonQuery();
   }
   catch(SqlException e)
     {
   Console.WriteLine(e.Message);
   }
   }
   }
   catch(Exception e)
   {
   Console.WriteLine(e.Message);
   }
   Console.ReadKey();
   }
Si vas a correr el código recuerda cambiar los datos subrayados en amarillo por los tuyos. Si deseas saber el nombre del servidor, abre SQL Server Management Studio y copia el contenido del campo "Nombre del Servidor" que aparece antes de iniciar sesión.

¿Como personalizo las sentencias con los datos recibidos por mi aplicación?

Bueno, esta es uno de los requerimientos mas importante en el desarrollo de una aplicación. Existen varias formas de realizarlo, pero yo te mostraré solo 4 para que elijas la que mas te convenga.
Primera
Supón que vas a eliminar a través de un formulario a un proveedor de tu Tienda de Ipods mediante el código. ¿Que se te ocurre?:
string textoCmd = "DELETE FROM IPOD WHERE ID = " + label_codigo.Text ;
SqlCommandcmd = new SqlCommand(textoCmd,con);
cmd.ExecuteNonQuery();
Muy bien!, esta sencilla forma permite construir por fracciones la cadena de nuestro comando. Concatenando el atributo Text del control label_codigo para obtener como resultado el string deseado.
¿Como conectar SQL Server con C#?Recuerda que si tu atributo es VARCHAR o DATE debes concatenar las comillas simples (' '). Esto evitará que se presenten errores SQL en tu aplicación.
Segunda
Es muy similar a la primera, solo que esta vez usaremos la función Format de String. Veamos:
string textoCmd = String.Format("DELETE FROM IPOD WHERE ID = {0}", label_codigo.Text);
Tercera
Usaremos el atributo Parameters de SqlCommand. Este atributo es una lista de objetos SqlParameter que permiten especificar el tipo de parámetro y su comportamiento. Introducimos el carácter '@'  en cada valor que deseamos reemplazar y luego lo referenciamos con el método Add() del atributo Parameters. Ejemplo:
string textoCmd = "DELETE FROM IPOD WHERE ID = @idIpod";
SqlCommandcmd = new SqlCommand(textoCmd,con);
cmd.Parameters.Add("@idIpod", SqlDbType.Int);
cmd.Parameters["@idIpod"].Value = Convert.ToInt32(label_codigo.Text);
El código anterior tiene varios conceptos interesantes. @idIpod es el parámetro que declaramos para referirnos al valor que introduciremos en nuestro comando. Luego usamos Add() con dos parámetros, el primero es el identificador declarado en el texto del comando y el segundo es el tipo de dato en SQL Server.
La clase SqlDbType proporciona la mayoría de tipos de dato que se presentan, en este caso el código del Ipod es INT. Y finalmente accedemos al parámetro para indicarle que use Text del Label. Obvio hicimos la conversión mediante la clase Convert para que todo sea acorde.
Cuarta
Esta ultima forma implementa también la clase SqlParameter  pero con una construcción previa. Esto permite dotar de nuevas características a los parámetros y así conseguir nuevas funcionalidades. En el siguiente código consultaremos cuantas Tablets tienen un precio mayor a N dolares:
Int32 precio;
stringtextoCmd = "SELECT @cantidad = COUNT(*) FROM TABLET "
+"WHERE PRECIO >@precio";
SqlCommand cmd = new SqlCommand(textoCmd,con);
SqlParameter p1 = newSqlParameter("@precio", Convert.ToInt32(textBox_precio.Text));
p1.Direction = ParameterDirection.Input;
SqlParameter p2 = newSqlParameter("@cantidad", null);
p2.Direction = ParameterDirection.Output;
p2.SqlDbType = SqlDbType.Int;
cmd.Parameters.Add(p1);
cmd.Parameters.Add(p2);
Esta vez usamos dos parámetros, una variable para guardar la cantidad de filas contadas y otra en la condición del WHERE. Para cada parámetro instanciamos un objeto SqlParameter definiendo todos sus atributos.
Esta vez hemos usado el atributo Direction asignándole una bandera del tipo ParameterDirection, el cual tiene como función indicar si el parámetro es de entrada o salida.Cuando un parámetro es de entrada significa que recibirá un valor establecido por el programador, si es de salida, este recibirá un valor producido por la sentencia T-SQL que construimos. En nuestro caso, usamos @cantidad para obtener la cantidad de Tablets que superan el precio dado por @precio.
Si deseas acceder al valor de @cantidad, entonces accedemos a los elementos del parámetro de forma vectorial, y asi obtener el atributo Value, el cual representa el valor contenido:
cmd.Parameters["@cantidad"].Value
Tu escoges que forma deseas usar. Mas adelante cuando incluyas comandos con mas columnas a consultar, te darás cuenta que una forma es mas cómoda que la otra. O cuando construyas métodos para tus formularios, notarás los distintos grados de complejidad y flexibilidad para cada forma. Todo es cuestión de necesidades.

¿Y como hacemos con los comandos que retornan en tablas?

Usaremos el método ExecuteReader() para leer cada fila del resultado de la consulta. Pero necesitamos una estructura de datos en donde leer dicha información. Para ello usaremos la clase SqlDataReader (también contenida en SqlClient) que nos proporciona la forma de lectura ideal. Veamos como acondicionar el código para recibir el resultado de un SELECT:
textoCmd = "SELECT Nombre,Apellido FROM HUESPED;";
SqlCommand cmd = new SqlCommand(textoCmd,con);
SqlDataReader reader = cmd.ExecuteReader();
Una vez referenciadas las filas de la consulta, procedemos a leer fila a fila mediante el método Read(). Este método cada vez que es invocado mueve la posición de lectura a la siguiente fila, por lo cual usaremos un bucle while para la lectura completa. Veamos:
            try
   {
   while (reader.Read())
   {
   Console.WriteLine(String.Format(" {0},{1}",
                                                  reader[0], reader[1]));
   }
   }
   catch(SqlException e)
   {
   Console.WriteLine(e.Message);
   }
   reader.Close();
Si pones atención en la linea de impresión de los datos del SqlDataReader, verás que estamos accediendo como si se tratase de un arreglo que guarda la fila  en las posiciones 0 y 1 (debido a que son solo dos columnas). Si fuesen mas columnas retornadas, entonces vas accediendo en orden secuencial a cada una de ellas de la misma forma.
Los objetos SqlDataReader deben cerrarse con el método Close() para desbloquear la memoria que están referenciando.

¿Quisiera mostrar los resultados de una consulta en un control DataGridView, como lo hago?

Te explicaré la forma que mas me gusta y hasta ahora me ha dado buenos resultados. Para ello usaremos la clase del namespace SqlCliente, llamada SqlDataAdapter, la cual administra nuestras filas como un bloque de datos en forma de tabla. Este formato este ideal para usar la clase DataTable y comunicar los resultados de la consulta con el DataGridView.
Analicemos el siguiente ejemplo, donde se consultan todos los registros que contiene una tabla llamada ESTUDIANTE:
DataTable datos = new DataTable();
stringtextoCmd = "SELECT * FROM ESTUDIANTE;";
SqlCommand cmd = newSqlCommand(textoCmd,con);
SqlDataAdapter adapter = newSqlDataAdapter(cmd);
adapter.Fill(datos);
dataGridView1.DataSource = datos;
Podemos destacar que hemos creado un objeto DataTable para hacer referencia a la información retornada por al consulta. El objeto adapter ha sido creado con un constructor que recibe como parámetro al comando asociado.
Y finalmente usamos el método Fill() que deposita de forma ordenada los registros en nuestra tabla, luego de ello asignamos la tabla al atributo DataSource del DataGridView, el cual contiene la información que se muestra en las celdas.

¿Se me olvidaba preguntarte...y como ejecuto procedimientos?

En este caso usaremos todas las herramientas que hasta el momento hemos visto. Para ejecutar un procedimiento debemos cambiar el atributo CommandType a StoredProcedure. En la cadena del comando usamos solo el nombre del procedimiento y añadimos todos los parámetros que tenga, ya sean de entrada, salida o entrada-salida.
A continuación veremos un ejemplo simple. Primero entra a SQL Server Management Studio, selecciona tu base de datos y ejecuta el siguiente procedimiento:
CREATE PROCEDURE impresion(@entrada INT)
AS
SELECT 'Tu parametro de entrada es:'+CAST(@entrada AS VARCHAR);
Ahora abre tu IDE favorita para C#, crea un nuevo proyecto y ejecuta la siguiente aplicación de consola:
static void Main(string[] args)
   {
         
   stringvalor;
   // Creamos una instancia de la clase SqlConnection
   string datosConexion = "Data Source = TuServidor;" +
   "Initial Catalog = Ejemplo ; Integrated Security = true;";
   using(SqlConnection con = new SqlConnection(datosConexion))
   {
     //Solicitamos un numero de ejemplo al usuario
   Console.Write("Digite su numero:");
   valor = Console.ReadLine();
   //Creamos el comando de tipo StoredProcedure
   SqlCommand cmd = new SqlCommand("impresion", con);
   cmd.CommandType = System.Data.CommandType.StoredProcedure;
   //Añadimos el parametro @entrada que establecimos en SQL Server
   SqlParameter p1 = new SqlParameter("@entrada", valor);
   p1.Direction = System.Data.ParameterDirection.Input;
   cmd.Parameters.Add(p1);
   try
   {
   //Abrimos la conexión
     con.Open();
   //Ejecutamos el comando
   SqlDataReader reader = cmd.ExecuteReader();
   //Retornó filas?, entonces leemos con Read()
   if (reader.HasRows)
   {
   while (reader.Read())
   {
   //Imprimimos el resultado del procedimiento
   Console.WriteLine(reader[0]);
     }
   }
   else
   {
   Console.WriteLine("El procedimiento no retorno ninguna fila");
   }
   }
   catch(SqlException e)
   {
   Console.WriteLine("/*--------------------------");
   Console.WriteLine(e.Message);
   Console.WriteLine("--------------------------*/");
   }
   System.Console.ReadKey();
   }
         
   }
Fíjate que en la sentencia try-catch usamos Excepciones tipo SqlException. Esto nos permite obtener los errores SQL en nuestra aplicación con todo detalle.
Espero este pequeño ejemplo te ayude a desarrollar requerimientos mas complejos en tu aplicación. Si tienes dudas, te invito a que las comentes que con gusto te ayudaré.
James Revelo Urrea - Desarrollador independiente

Volver a la Portada de Logo Paperblog