ExecuteScalar ничего не возвращает из хранимой процедуры

У меня есть простая хранимая процедура, которая возвращает type_id (int) в качестве параметра OUTPUT, используя type_name (varchar (100)) в качестве входного параметра. Когда я выполняю хранимую процедуру в SqlServer, она работает нормально и возвращает соответствующий type_id.

CREATE PROCEDURE [dbo].[intake_types_select_by_type_name]
    @type_name varchar(100),
    @type_id integer OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON

    SELECT  @type_id = type_id
    FROM    intake_types
    WHERE   type_name = @type_name
END

Однако, когда я вызываю хранимую процедуру из функции в VisualStudio, параметр @type_id возвращает Nothing.

Public Shared Function sp_intake_types_select_by_type_name(ByVal conn As DBConnection, ByVal caseName As String) As Integer
    Dim sp As SqlCommand = conn.GetStoredProcedure("intake_types_select_by_type_name")
    With sp.Parameters
        .Add("@type_name", SqlDbType.VarChar, ParameterDirection.Input).Value = caseName
        .Add("@type_id", SqlDbType.Int)
        .Item("@type_id").Direction = ParameterDirection.Output
    End With
    sp.ExecuteScalar()
    If Not IsDBNull(sp.Parameters("@type_id").Value) Then
        Return sp.Parameters("@type_id").Value
    Else
        Return Nothing
    End If
End Function

Я часами искал в Интернете решение, но ничего не нашел. У меня есть аналогичная хранимая процедура и функция в другом проекте, который использует ту же кодировку и логику, и он отлично работает (однако возвращает varchar в качестве параметра OUTPUT). Я сравнил эти два проекта, чтобы увидеть, не упускаю ли я чего-то простого, но не радости.


person Michael Callas    schedule 15.02.2017    source источник
comment
Вы пробовали Dim result = sp.ExecuteScalar()?   -  person Fabio    schedule 15.02.2017
comment
Каждый пример, который я видел, делает что-то вроде TypeID = (Int32) sp.ExecuteScalar(). Вы уверены, что в другом своем проекте вы просто звоните sp.ExecuteScalar()?   -  person Aaron Bertrand    schedule 15.02.2017
comment
удалите .Item("@type_id").Direction = ParameterDirection.Output, измените .Add("@type_id", SqlDbType.Int) на .Add("@type_id", SqlDbType.Int, ParameterDirection.Output) и измените ExecuteScalar на ExecuteNonQuery(). Не знаю, решит ли это вашу проблему, но попробовать стоит.   -  person Zohar Peled    schedule 15.02.2017
comment
ExecuteScalar возвращает что-то ЕСЛИ у вас есть SELECT в вашей хранимой процедуре, которая выбирает (и, таким образом, возвращает) ровно одну строку, один столбец. С другой стороны, вы присваиваете это значение выходному параметру - это НЕ будет возвращено ExecuteScalar!   -  person marc_s    schedule 15.02.2017
comment
@ Аарон Бертран - Да, он звонит и работает, но теперь я не уверен, почему. Я буду обновлять и эту.   -  person Michael Callas    schedule 15.02.2017
comment
@Zohard Peled - пробовал это раньше, и это не сработало. Спасибо, в любом случае.   -  person Michael Callas    schedule 15.02.2017
comment
@marc_s - отличная информация! Спасибо.   -  person Michael Callas    schedule 15.02.2017
comment
@AaronBertrand Вы заметили проблему, уникальную для настроек по умолчанию в VB.NET, где задействуется доступность преобразования типа во время выполнения (также известного как позднее связывание). Option Strict On отключает это. Множество мемов meta: Option Strict On.   -  person Andrew Morton    schedule 15.02.2017


Ответы (1)


Вам действительно следует использовать Option Strict On и исправить указанные в нем проблемы. В настоящий момент выполняются неявные преобразования между типами данных, которые сделают ваш код намного медленнее и, возможно, подверженным ошибкам.

Предлагаю следующее:

CREATE PROCEDURE [dbo].[intake_types_select_by_type_name]
    @type_name varchar(100)
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;

    SELECT  type_id
    FROM    intake_types
    WHERE   type_name = @type_name
END

И функция для его вызова:

Option Strict On
' ....'

Public Shared Function sp_intake_types_select_by_type_name(ByVal conn As DBConnection, ByVal caseName As String) As Integer
    Dim sp As SqlCommand = conn.GetStoredProcedure("intake_types_select_by_type_name")
    sp.Parameters.Add("@type_name", SqlDbType.VarChar, 100).Value = caseName

    Dim result As Object = sp.ExecuteScalar()

    If result Is Nothing Then
        Return -1 ' check for -1 in the calling code '
    Else
        Return CInt(result)
    End If

End Function

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

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

Ссылка: Выполнить скаляр, чтобы отловить ошибку в случае отсутствия возвращенных записей.

person Andrew Morton    schedule 15.02.2017
comment
Эндрю, большое спасибо за советы, отличные объяснения, и рабочее решение. Option Strict On обнаружил еще два неявных преобразования, которые я исправил. Далее я буду изучать использование / удаление SQL-соединения вместо того, чтобы использовать одно SQL-соединение для всего. Я очень ценю ваше время и большой вклад! - person Michael Callas; 15.02.2017
comment
@MichaelCallas Пожалуйста :) Я настоятельно рекомендую установить Option Strict On по умолчанию для новых проектов, если вы еще этого не сделали. Что касается другого ответа в сообщении, на которое я ссылался, я действительно протестировал фрагмент VB на реальной базе данных, чтобы подтвердить, что Is Nothing работает, а Is DbNull.Value дает ложные результаты, на всякий случай, если вы читаете дальше. - person Andrew Morton; 15.02.2017
comment
Эндрю, у меня есть настройки по умолчанию для новых проектов, но это унаследованный проект, поэтому я рад, что вы указали на это. Я также прочитал ссылку в вашем сообщении, поэтому спасибо за продолжение. Полезно знать. - person Michael Callas; 15.02.2017