Объект не может быть преобразован из DBNull в другие типы в хранимой процедуре с выходным параметром

Я хочу использовать хранимую процедуру с использованием SQL Server в приложении C#, которое возвращает выходной параметр, но попробовав много разных способов, я не понял, как это сделать.

Эта процедура должна возвращать переменную/параметр под названием «recargo» (доплата), значение которой зависит от возраста, пола и семейного положения клиента. Процедура также имеет входной параметр, который является идентификатором клиента.

Приложение возвращает исключение DBNull, которое я уже пытался исправить с помощью кода, но оно по-прежнему не возвращает значение. Он всегда возвращает ноль.

CREATE OR ALTER PROCEDURE CalcularRecargo
    @rut NVARCHAR(10),
    @recargo DECIMAL OUTPUT
AS
BEGIN
    DECLARE @fecha DATETIME, 
            @sexo TINYINT, 
            @ecivil TINYINT,
            @edad INT

    SELECT 
        @fecha = cl.FechaNacimiento, 
        @sexo = cl.IdSexo, 
        @ecivil= cl.IdEstadoCivil 
    FROM 
        Contrato c 
    JOIN 
        Cliente cl ON c.RutCliente = cl.RutCliente 
    WHERE 
        c.RutCliente = @rut

    --HERE I CALCULATE THE AGE, I DON'T KNOW IF THIS IS CORRECT
    BEGIN      
        SET @edad = (CONVERT(INT, CONVERT(CHAR(8), GETDATE(), 112)) - CONVERT(CHAR(8), @fecha, 112)) / 10000
    END

    --if the age is between some of these ranges, the surcharge (@recargo) should be increased.
    IF @edad >= 18 AND @edad <=25 
    BEGIN 
        SET @recargo = @recargo + 3.6 
    END
    ELSE IF @edad >=26 AND @edad <=45 
    BEGIN 
        SET @recargo = @recargo + 2.4 
    END
    ELSE 
    BEGIN 
        SET @recargo = @recargo + 6 
    END

    --same with gender.
    IF @sexo = 1 
    BEGIN 
        SET @recargo = @recargo + 2.4 
    END
    ELSE 
    BEGIN 
        SET @recargo = @recargo + 1.2 
    END

    --same with marital status
    IF @ecivil = 1 
    BEGIN 
        SET @recargo = @recargo + 4.8 
    END
    ELSE IF @ecivil = 2 
    BEGIN 
        SET @recargo = @recargo + 2.4 
    END
    ELSE 
    BEGIN 
        SET @recargo = @recargo + 3.6 
    END

    RETURN
END;

Вот код C# метода:

public static double  CalcularRecargo(string rut)
{
    double recargo = 0.0;
    SqlConnection conexion = new SqlConnection(ConSql.conexion);

    try
    {
        conexion.Open();

        SqlCommand cmd = new SqlCommand("CalcularRecargo", conexion);
        cmd.CommandType = CommandType.StoredProcedure;

        SqlParameter ParRut = new SqlParameter("@rut", SqlDbType.VarChar);
        ParRut.Value = rut;
        cmd.Parameters.Add(ParRut);

        SqlParameter ParRecargo = new SqlParameter("@recargo", SqlDbType.Decimal);
        //ParRecargo.Direction = ParameterDirection.Output;
        cmd.Parameters.Add(ParRecargo).Direction=ParameterDirection.Output;

        cmd.ExecuteNonQuery();

        // IF I UNCOMMENT AND USE THIS CODE IT STILL RETURNS A NULL.
        var prerecargo = cmd.Parameters["@recargo"].Value;

        if (prerecargo != DBNull.Value)
           @recargo = Convert.ToDouble(prerecargo);

        // IF I UNCOMMENT AND USE THE CODE BELOW IT RETURNS THE DBNULL EXCEPTION
        // recargo = Convert.ToDouble(cmd.Parameters["@recargo"].Value);
    }
    catch(Exception error)
    {
        MessageBox.Show(error.Message);
    }
    finally
    {
         conexion.Close();
    }

    return recargo;
}

И вот другая часть кода С#, где я его реализую:

private void button1_Click(object sender, EventArgs e)
{
    double recargo = 0;
    double primaanual = 0;
    double primamensual = 0;

    if (ComboTitular.Text != "")
    {
        recargo = Contrato.CalcularRecargo(ComboTitular.Text);
    }

    if (recargo > 0 && ComboPlan.Text != "")
    {
        primaanual = Plan.planes.Find(i => i.Nombre == ComboPlan.Text).PrimaBase + recargo;
        primamensual = primaanual / 12;

        LblPrimaAnual.Text = primaanual.ToString();
        LblPrimaMensual.Text = primamensual.ToString();
    }
    else
    {
        MessageBox.Show("Seleccione un plan por favor");
    }

    try
    {
        Contrato con = new Contrato(numcontrato, feccreacion, fectermino, ComboTitular.Text, ComboPlan.Text, poliza, inivig, finvig, estavig, declarasalud, primaanual, primamensual, observacion);

        string resultado = con.AgregarContrato(con);

        MessageBox.Show(resultado);
    }
    catch (Exception error)
    {
        MessageBox.Show("Contract already exists");
    }
}

ПРИМЕЧАНИЕ: я удалил много лишнего кода, чтобы сделать вопрос более понятным.

заранее спасибо


person Runsis    schedule 09.07.2018    source источник


Ответы (1)


Я думаю, проблема в том, что @recargo имеет значение NULL, потому что ему никогда не присваивается начальное значение перед вычислениями.

Ниже вы найдете ссылку на быструю репродукцию и документацию для более подробной информации:

Все арифметические операторы (+, -, *, /, %), побитовые операторы (~, &, |) и большинство функций возвращают null, если любой из операндов или аргументов равен null, за исключением свойство IsNull Дополнительные сведения см. на странице https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/handling-null-values

DECLARE @recargo DECIMAL

-- @recargo is NULL
SELECT @recargo


SET @recargo = @recargo + 2.4 
SELECT @recargo --NULL

SET @recargo = 0 --INITIALIZING THE VARIABLE
SET @recargo = @recargo + 2.4

-- @recargo is 2
SELECT @recargo
person Evandro Paula    schedule 09.07.2018
comment
Спасибо! Проблема была в том, что я не инициализировал @recargo. Исправлена ​​запись SET recargo = 0 (@before) перед операторами IF-ELSE. Хороший ответ - person Runsis; 09.07.2018