none
Ayuda T-SQL y C# (control de excepciones) RRS feed

  • Pregunta

  • Hola, buenas tardes, soy nuevo con C# y T-SQL, quisiera pedirles una consulta que llevo días tratando de resolver y nada aún. Tengo instalado SQL Server 2017 y Visual Studio Enterprise 2019. tengo la siguiente situación:

    Tengo en SQL Server creado la siguiente tabla:

    create table productos(
        ID int primary key,
        CATEGORIA varchar(30) not null,
        MARCA varchar(30) not null,
        MODELO varchar(30) not null,
        CLASIFICACION varchar(30) not null,
        SUBCLASIFICACION varchar(30) not null,
        DESCRIPCION varchar(250) not null
    );

    También tengo el siguiente procedimiento almacenado:

    ALTER PROCEDURE [dbo].[ModificarProducto]
    @ID int,
    @CATEGORIA varchar(30),
    @MARCA varchar(30),
    @MODELO varchar(30),
    @CLASIFICACION varchar(30),
    @SUBCLASIFICACION varchar(30),
    @DESCRIPCION varchar(250),
    @RESULT int output
    AS
    BEGIN
    SET NOCOUNT ON;
    BEGIN TRANSACTION
    BEGIN TRY     
    select 1/0;
    COMMIT TRANSACTION
    END TRY
    BEGIN CATCH
        set @RESULT=ERROR_NUMBER();
    IF (XACT_STATE()) = -1
    ROLLBACK TRANSACTION; 
    IF (XACT_STATE()) = 1
    COMMIT TRANSACTION
    return @RESULT;
    END CATCH
    END

    Luego en C# tengo una clase llamada Datos que fue creada en el ficheros Datos.cs como se muestra a continuación:

    //Fichero Datos.cs

    class Datos
    {

            private string CadenaConexion = "Data Source=SERVER; Initial Catalog=BD; Integrated Security=True";
            private SqlConnection conn;
            private DataTable dt;

            public Datos()
            {
                this.conn = new SqlConnection(this.CadenaConexion);
            }

           public int Modificar(int id, string categoria, string marca, string modelo, string clasificacion, string subclasificacion, string descripcion)
            {
                int resultado=0;
                dt = new DataTable();
                string query = "ModificarProducto";
                SqlCommand cmd = new SqlCommand(query, this.conn);
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add(new SqlParameter("@ID", id));
                cmd.Parameters.Add(new SqlParameter("@CATEGORIA", categoria));
                cmd.Parameters.Add(new SqlParameter("@MARCA", marca));
                cmd.Parameters.Add(new SqlParameter("@MODELO", modelo));
                cmd.Parameters.Add(new SqlParameter("@CLASIFICACION", clasificacion));
                cmd.Parameters.Add(new SqlParameter("@SUBCLASIFICACION", subclasificacion));
                cmd.Parameters.Add(new SqlParameter("@DESCRIPCION", descripcion));
                cmd.Parameters.Add(new SqlParameter("@RESULT", resultado));
                SqlDataAdapter adap = new SqlDataAdapter(cmd);
                adap.Fill(dt);
                resultado = Convert.ToInt32(cmd.Parameters["@RESULT"].Value);
                //MessageBox.Show(resultado.ToString());
                conn.Close();
                return resultado;
            }        
    }


    Tengo además un formulario creado en el fichero Form3.cs con el siguiente método:

    //Fichero Form3.cs

    private void btnGuardar_Click(object sender, EventArgs e) 
    {
                int resultado = 0;
                try
                {
                    resultado = datos.Modificar(
                                            System.Convert.ToInt32(txtId.Text), 
                                            txtCategoria.Text, 
                                            txtMarca.Text, 
                                            txtModelo.Text, 
                                            txtClasificacion.Text, 
                                            txtSubclasificacion.Text, 
                                            txtDescripcion.Text
                                        );
                    if (resultado!=0) 
                        throw new Exception("No se pudo modificar los datos");
                }catch (Exception exception1) {
                    StringBuilder errorMessages = new StringBuilder();
                    errorMessages.Append(
                                    "Número de error: " + resultado.ToString() + "\n"+
                                    "Mensaje: " + exception1.Message + "\n"
                                        ); 
                    MessageBox.Show(
                                        errorMessages.ToString(), 
                                        "Error", 
                                        MessageBoxButtons.OK, 
                                        MessageBoxIcon.Error
                    );
                }
                Close();
    }


    NOTA: lo que pretendo es lo siguiente:
    1- Que el procedimiento almacenado devuelva en el parámetro @RESULT el número de error de división por cero.
    2- El valor devuelto en ese parámetro asignárselo a la variable local "resultado" en el método Modificar de la clase Datos.
    3- Devolver en el método Modificar de la clase Datos el valor de la variable "resultado".
    4- Lanzar una excepción en el evento btnGuardar_Click con el número del error obtenido y un mensaje personalizado.

    Alguien me puede ayudar. Al parecer nunca entra al bloque "catch (Exception exception1)", sino que pasa directamente al "Close();", al parecer pq en el sp nunca devuelve el valor del error en el parámetro @RESULT.
    sábado, 17 de octubre de 2020 19:58

Todas las respuestas

  • Tienes al menos un problema en el código, que es el siguiente: Estás añadiendo @RESULT como parámetro de tipo INPUT, que es lo que hace por defecto el método Add. Pero tal como lo devuelve el procedimiento, tienes que recuperarlo como tipo SqlParameter.ReturnValue. Se lo puedes indicar en la propiedad ParameterType del parámetro:

    SqlParameter resultParam = new SqlParameter("@RESULT", SqlDbType.Int);
    resultParam.ParameterType = ParameterType.ReturnValue;
    cmd.Parameters.Add(resultParam);

    Por cierto, lo has declarado como OUTPUT en el procedimiento pero lo estás devolviendo con la palabra Return. Las dos cosas son incompatibles: O es de tipo Output y entonces solo se le asigna valor con un Set y no hay que poner Return, o solo lo declaras como variable interna y no como argumento y entonces se devuelve con Return. Si usas la primera opción entonces el tipo es ParameterType.Output; en el segundo caso es ParameterType.ReturnValue, que es lo que te he puesto en el ejemplo.

    EDITADO: Por cierto, no es muy razonable usar la excepción si la estás tratando en el mismo método en el que comparas if (result!=0). Lo lógico sería directamente en ese "if" dar el mensaje de error, en lugar de lanzar una excepción y capturarla justo debajo para mostrar el mensaje. Usar la excepción tendría sentido si hicieses el throw en la capa de datos y lo capturases más arriba, pero no como lo estás haciendo.


    sábado, 17 de octubre de 2020 20:15
  • Me da error en esta línea 

    resultParam.ParameterType = ParameterType.Output;

    dice que SqlParameter no contiene una definición para "ParameterType"

    sábado, 17 de octubre de 2020 22:37
  • uso sql server 2017
    sábado, 17 de octubre de 2020 22:37
  • ya resolví el problema, por si alguien le sirve aquí viene un ejemplo que me ayudó a corregir los errores que tenía en mi código, un saludo

    https://blog.cloudboost.io/how-to-use-sql-output-parameters-in-stored-procedures-578e7c4ff188

    sábado, 17 de octubre de 2020 23:11
  • https://blog.cloudboost.io/how-to-use-sql-output-parameters-in-stored-procedures-578e7c4ff188
    sábado, 17 de octubre de 2020 23:12
  • resultParam.ParameterType = ParameterType.Output;

    Perdón, fallo mío al escribir el código de memoria. La propiedad se llama Direction en lugar de ParameterType, y el valor es igualmente ParameterDirection.
    domingo, 18 de octubre de 2020 7:27