SqlException
Manejo de excepciones especializadas.

Fecha: 02/Feb/2005 (1 Febrero 2005)
Autor:
Misael Monterroca, mmonterroca@neo-mx.com

 


 

Una ventaja del manejo de excepciones es el poder manejar excepciones especializadas o personalizadas, con lo cual, podemos realizar un mejor manejo en el flujo de nuestra aplicación al poder reaccionar ante un error especifico y de ésta manera realizar acciones especificas.

Como ejemplo, utilizaremos la excepción SqlException, mediante la cual, podemos recuperar los códigos de error que Sql Server genera internamente, éstos errores van desde un "Acceso denegado al usuario x" o "Nombre de objeto invalido x" por poner un ejemplo.

SqlException nos expone  a través de su propiedad SqlException.Errors una colección que contiene uno o más objetos del tipo SqlError, con los cuales, podremos conocer el error exacto que ocurrio en el servidor, utilizando ésta información construiremos una aplicación a manera de ejemplo, mediante la cual controlaremos los errores basicos que suceden al momentos de realizar la conexión hacia la base de datos.

La aplicación nos pedira los siguientes datos, mediante los cuales esperaremos una determinada excepción de sql.

Comunmente dentro del manejo de errores unicamenye incluimos el manejador de la excepción "general" Exception ,pero en esta ocasión dentro de nuestro bloque Try, incluiremos una excepción más SqlException .

try
{
    //hacer algo
}
catch (SqlException
ex)
{
//hacer algo  

}
catch(Exception ex)
{
//hacer algo
}

Aún cuando la única excepción que intentamos utilizar será SqlException, es recomendable dejar el manejador de las excepciones generales, ya que no siempre (dependiendo de la implementación del bloque try) las exepciones internas serán propiciadas por Sql, aunque insisto, eso depende por mucho de la implementación que se tenga.

Si unicamente intentaramos detectar la ejecución a manera general, bastaria con obtener el mensaje de error que genero Sql Server SqlException.Message, para este ejemplo entraremos un poco más en detalle y obtendremos el error detallado generado en el servidor, para ésto, tendremos que realizar un for dentro de nuestro bloque Catch para asi obtener todos los errores generados.

catch (SqlException ex)
{
    foreach (SqlError sError in ex.Errors)
    {
        //Implementación.....

La clase que realmente contiene la información detallada del error generado es SqlError, para obtener el código de error generado utilizaremos la propiedad number SqlError.Number, éste código de error es que el Sql Server genero internamente, para ver todos los códigos de error posibles consulte la documentación de Sql Server. Si bien, en este ejemplo nos limitamos a reemplazar el mensaje de error generado por uno más especifico para el usuario, esta no es la única utilidad que podemos obtener, por ejemplo, en caso de que el usuario y password sean incorrectos, podriamos guardar una bitacora con el usuario que esta intentando acceder a nuestra base de datos.

Por ejemplo, si intentaramos acceder a un servidor y éste no fuera encontrado se generaria un Error 17, estos errores los capturaremos dentro de nuestro bloque switch

      switch (sError.Number)
                    {
                        case 17:
                        MessageBox.Show("El servidor '" + servidor  + "' no existe, por favor verifique el nombre");
                        break ;

 

Si el usuario y password fueran incorrectos, el error generado seria el numero 18456, hay que tener especial antención en el caso de que se generen más de 1 error, tal es el caso de un usuario el cual este tratando de acceder a una base de datos en la cual no tiene permisos, para éste escenario primero se generaria un error 4060, el cual pertenece a //Cannot open database requested in login '%.*ls'. Login fails. pero tambien se generara un error 18456 //Login failed for user '%ls'. ya que efectivamente, el acceso fallo, para este caso se pueden tener dos posibles acciones, informar de un error y salir del bucle, o informar de ambos errores.

Por ultimo, la clase SqlError nos provee de varia información, la cual nos puede llegar a ser útil, para el caso del error 208 //Invalid object name '%ls'." Nombre de objeto invalido, este error podria darse por ejemplo, dentro de una vista, pero para tener una mayor referencia el objeto SqlError, nos da la información de la linea en la cual ocurrio el error SqlError.LineNumber, al final de cuentas SqlError nos provee de la misma información que si ocurria un error al ejecutar una sentencia desde el Analizador de Consultas.

Servidor: mensaje 208, nivel 16, estado 1, línea 1 El nombre de objeto 'sysobject' no es válido.

using System;
using System.Data.SqlClient;
using System.Windows.Forms;

namespace ExcepcionesSql
{
    /// <summary>
    /// Summary description for DB.
    /// </summary>
    public class DB
    {
        /// <summary>
        /// Realiza la conexión a un servidor Sql Server e intenta ejecutar un determinado comando.
        /// </summary>
        /// <param name="servidor">Nombre del servidor</param>
        /// <param name="usuario">Nombre de usuario</param>
        /// <param name="clave">Password del usuario</param>
        /// <param name="basedatos">Base de datos en el servidor sql</param>
        /// <param name="sqlText">Un comando Sql (Select)</param>
        public DB(string servidor,string usuario,string clave,string basedatos,string sqlText)
        {
            // Se crea la variable que contiene el string de conexiòn
            string adoCon = String.Format("Data Source={0};Initial Catalog={1};User Id={2};Password={3}",servidor,basedatos,usuario,clave);

            try
            {   
                using (SqlConnection sCon = new SqlConnection(adoCon))
                {
                    sCon.Open();   
                    MessageBox.Show("Se realizo la conexión");
                    SqlCommand sComando = new SqlCommand(sqlText,sCon);
                    sComando.ExecuteNonQuery();//SOlo para comprobar que la sentencia puede ejecutarse.
                    MessageBox.Show("Se realizo la consulta");
                }


            }
            catch (SqlException ex)
            {

                foreach (SqlError sError in ex.Errors)
                {

                    switch (sError.Number)
                    {
                        case 17:
                        MessageBox.Show("El servidor '" + servidor  + "' no existe, por favor verifique el nombre");
                        break;
                        case 18452:    //Login failed for user '%ls'. Reason: Not associated with a trusted SQL Server connection.
                        MessageBox.Show("Especifique un usuario y password");
                        break;
                        case 18456: //Login failed for user '%ls'.
                        MessageBox.Show("El usuario o password es incorrecto");
                        break;   
                        case 4060:    //Cannot open database requested in login '%.*ls'. Login fails.
                        MessageBox.Show("El usuario no tiene permisos para acceder a la base de datos '" + basedatos + "'");
                        break;   
                        case 208:    //Invalid object name '%ls'."
                        MessageBox.Show("El nombre del objeto es incorrecto, para mayor información consulte la lnea :" + sError.LineNumber);
                        break;
                        default:
                        MessageBox.Show(sError.Message);
                        break;
                    }


                }
            }
            catch(Exception ex)
            {   
                MessageBox.Show(ex.Message);
            }

        }
    }
}


Espacios de nombres usados en el código de este artículo:

System.Data.SqlClient

 


ir al índice

Fichero con el código de ejemplo: neo_mx_SqlException.zip - 20.3 KB