SqlParameter исчезает, когда ему присваивается значение null?

Недавно я наткнулся на следующую проблему:

Присвоение null свойству Value SqlParameter, по-видимому, приводит к исключению параметра из запроса вместо передачи NULL. Это приводит к потенциально вводящему в заблуждение сообщению об ошибке, когда хранимая процедура не имеет значения по умолчанию для параметра.

У меня есть таблица базы данных со столбцом, который принимает NULL.

USE [TheDatabase]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[TheTable](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [TheValue] [varchar](50) NULL,
 CONSTRAINT [PK_TheTable] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

Таблица заполняется с помощью хранимой процедуры. Обратите внимание, что параметр, предназначенный для столбца, не указывает значение по умолчанию.

USE [TheDatabase]
GO

CREATE PROCEDURE TheStoredProcedure
    @TheValue varchar(50)
AS
BEGIN
    INSERT INTO TheTable (TheValue) VALUES (@TheValue);
END
GO

Выполнение хранимой процедуры со значением и с NULLработает нормально:

USE [TheDatabase]
GO

EXEC [dbo].[TheStoredProcedure] @TheValue = 'A string for Algernon'
GO

EXEC [dbo].[TheStoredProcedure] @TheValue = NULL
GO

Я запускаю следующий код:

using System;
using System.Data;
using System.Data.SqlClient;

class Program
{
    static void Main(string[] args)
    {
        ExecuteStoredProcedure("A string for Algernon");

        ExecuteStoredProcedure(DBNull.Value);

        ExecuteStoredProcedure(null);

        Console.ReadKey();
    }

    private static void ExecuteStoredProcedure(object value)
    {
        using (var connection = new SqlConnection("Server=TheServer;Database=TheDatabase;Persist Security Info=False;Integrated Security=true;"))
        {
            connection.Open();
            using (var sqlCommand = new SqlCommand("TheStoredProcedure", connection) {CommandType = CommandType.StoredProcedure})
            {
                sqlCommand.Parameters.Add(new SqlParameter("@TheValue", SqlDbType.VarChar, 50) {Value = value});

                try
                {
                    sqlCommand.ExecuteNonQuery();
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }
        }

        Console.WriteLine(DateTime.Now);
    }
}

Первые два вызова метода выполняются нормально: первый добавляет строку в таблицу, второй добавляет NULL, но вызов ExecuteStoredProcedure() с null приводит к следующей ошибке:

System.Data.SqlClient.SqlException (0x80131904): процедура или функция «TheStoredProcedure» ожидает параметр «@TheValue», который не был предоставлен.

Из документации следует, что вы должны использовать null, если хотите использовать значение параметра по умолчанию в хранимой процедуре.

Этому свойству можно присвоить значение null или Value. Используйте значение, чтобы отправить значение NULL в качестве значения параметра. Используйте null или не устанавливайте Value, чтобы использовать значение по умолчанию для параметра.

https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlparameter.value?view=netframework-4.7.2#System_Data_SqlClient_SqlParameter_Value

Поскольку мой параметр не имеет значения по умолчанию, следует ожидать какой-то ошибки. Однако то, что мы получаем, кажется вводящим в заблуждение: ошибка, указывающая на то, что параметр не был получен на стороне базы данных, в отличие от ошибки, указывающей на неожиданное значение (или, скорее, на отсутствие значения) этого параметра.

Или это на самом деле ожидаемое поведение, когда установка значения null эффективно исключает передачу параметра на сервер при выполнении команды?

Обновление от 20 января 2010 г.

С тех пор документация была обновлена:

Для этого свойства можно установить значение null или DBNull.Value. Используйте DBNull.Value, чтобы отправить значение NULL в качестве значения параметра. Используйте null или не устанавливайте Значение, чтобы использовать для параметра значение по умолчанию.

Возможно, это не кристально ясно, но, по крайней мере, недвусмысленно.

Ключевым здесь является то, что значение параметра по умолчанию, определенное для хранимой процедуры, вступает в игру, если объект SqlParamer имеет null для свойства Value, и я могу только предположить, что это достигается «за кулисами», опуская его из выполнение запроса.

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

-S


person Sigurd Garshol    schedule 07.12.2018    source источник
comment
См. ответ здесь stackoverflow.com/questions/2113122/   -  person Roma Ruzich    schedule 07.12.2018
comment
Как указал @RomaRuzich, ваш сохраненный процесс имеет параметр, который не имеет значения по умолчанию. Установите @TheValue varchar(50) = NULL в своей хранимой процедуре, чтобы она работала со значением по умолчанию на стороне БД.   -  person shahkalpeshp    schedule 07.12.2018
comment
Используйте DbNull.Value вместо передачи null   -  person Panagiotis Kanavos    schedule 07.12.2018
comment
@shahkalpesh это не решение. Это скрывает проблему, а не решает ее   -  person Panagiotis Kanavos    schedule 07.12.2018
comment
@PanagiotisKanavos: я полагаю, OP хочет передать нулевое значение процедуре, у которой нет параметра по умолчанию. Это достигается передачей DBNull.Value. Однако если вы передадите null (c#), это не будет рассматриваться как DBNull.Value. Как вы думаете, что ищет ОП и как вы этого добиваетесь?   -  person shahkalpeshp    schedule 07.12.2018
comment
@shahkalpesh, почему тогда вы предложили OP изменить хранимую процедуру?   -  person Panagiotis Kanavos    schedule 07.12.2018
comment
@PanagiotisKanavos: Потому что я думаю, что именно так все и решается. Как вы думаете, почему он скрывает проблему, а не решает ее?   -  person shahkalpeshp    schedule 07.12.2018
comment
@shahkalpesh вам не нужно изменять хранимую процедуру только для того, чтобы установить для параметра значение null. Вместо этого вы используете DbNull.Value. Нет, почему ты думаешь об этом. Это то, что говорят документы, даже если они, кажется, имеют опечатку   -  person Panagiotis Kanavos    schedule 07.12.2018
comment
Возможный дубликат Назначить null параметру SqlParameter   -  person Panagiotis Kanavos    schedule 07.12.2018
comment
@shahkalpesh на самом деле есть несколько повторяющихся вопросов по этому поводу   -  person Panagiotis Kanavos    schedule 07.12.2018
comment
Возможный дубликат Лучший метод присвоения значения NULL параметру SqlParameter   -  person Panagiotis Kanavos    schedule 07.12.2018
comment
@PanagiotisKanavos: у OP уже есть пример передачи DBNull.Value, который заботится. Я не уверен, как передать нулевое значение (С#), кроме использования DBNull.Value?   -  person shahkalpeshp    schedule 07.12.2018


Ответы (2)


Не устанавливайте значение null, если вы хотите передать NULL хранимой процедуре

Документы почти правильные. Во фразе:

Используйте значение для отправки значения NULL в качестве значения параметра.

Ссылка указывает на DbNull.Value. Это ошибка документации

Передача null вместо DbNull.Value означает, что вы хотите использовать параметр хранимой процедуры по умолчанию. Эта часть документов верна:

Используйте null или не устанавливайте Value, чтобы использовать значение по умолчанию для параметра.

Обновить

Я только что добавил проблему Github для этого

person Panagiotis Kanavos    schedule 07.12.2018

SqlParameter. Значение свойства

Для этого свойства можно задать значение null или DBNull.Value. Используйте DBNull.Value для отправки значения NULL в качестве значения параметра. Используйте null или не устанавливайте Value, чтобы использовать значение по умолчанию для параметра.

Итак, для value = null вы должны установить значение по умолчанию для вашего параметра.

CREATE PROCEDURE TheStoredProcedure
    @TheValue varchar(50) = NULL
person Denis Rubashkin    schedule 07.12.2018