ADO. NET SqlCommand не возвращает правильное значение для SCOPE_IDENTITY () - PullRequest
0 голосов
/ 07 января 2020

У меня есть простая таблица:

CREATE TABLE [MyTable]
(
    [ID] [INT] IDENTITY(1,1) NOT NULL,
    [NAME] [NVARCHAR](100) NOT NULL,
    [DATA] [NVARCHAR](max) NOT NULL,

    CONSTRAINT [PK_MyTable] 
        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] TEXTIMAGE_ON [PRIMARY]
GO

У меня есть хранимая процедура, которая вставляет строку в эту таблицу и возвращает значение столбца PK IDENTITY через SCOPE_IDENTITY()

CREATE PROCEDURE [InsertMyTable] 
    @NAME NVARCHAR(100),
    @DATA NVARCHAR(MAX)  
AS
BEGIN
    INSERT INTO [MyTable] ([NAME], [DATA])
    VALUES (@NAME, @DATA)  

    RETURN SCOPE_IDENTITY()
END

Я протестировал эту хранимую процедуру с помощью SSMS и других инструментов, и она работает как положено.

Каждый раз, когда запускается следующий скрипт, возвращаемое значение увеличивается на 1:

DELETE FROM [MyTable]
GO

DECLARE @return INT

EXEC @return = [InsertMyTable] @NAME='MyName', @DATA='Data'

SELECT @return
GO
  • Первый запуск возвращает 1
  • Второй запуск возвращает 2
  • et c.

Однако, когда я выполняю аналогичные действия в C# коде, используя System.Data.SqlClient возвращаемое значение хранимой процедуры всегда равно 1. Кажется, что это не означает, что идентичность увеличивается при каждом удалении и повторной вставке строки.

    using (var connection = new SqlConnection({connection-string}))
    using (var command = connection.CreateCommand())
    {
        command.CommandType = CommandType.Text;
        command.CommandText = "delete from [MyTable] where [NAME]='MyName'";

        connection.Open();

        command.ExecuteNonQuery();
    }

    using (var connection = new SqlConnection({connection-string}))
    using (var command = connection.CreateCommand())
    {
        command.CommandType = CommandType.StoredProcedure;
        command.CommandText = "InsertMyTable";

        command.Parameters.Add(new SqlParameter($"@NAME", "MyName"));
        command.Parameters.Add(new SqlParameter($"@DATA", "data"));

        connection.Open();

        var returnValue = command.ExecuteNonQuery();
    }
  • Первый запуск returnValue = 1
  • Второй запуск returnValue = 1
  • et c.

1 Ответ

3 голосов
/ 07 января 2020

Ваша хранимая процедура возвращает идентификатор как возвращаемое значение хранимой процедуры, которое следует использовать только для успеха или неудачи, а не для данных.

SqlCommand.ExecuteNonQuery () возвращает сумму всех сообщений подсчета строк для выполненного пакета.

Необходимо использовать выходной параметр

CREATE PROCEDURE [InsertMyTable] 
  @NAME NVARCHAR(100)
, @DATA NVARCHAR(MAX) 
, @ID INT OUTPUT
AS
BEGIN
INSERT INTO [MyTable]
           ([NAME]
           ,[DATA])
     VALUES
           ( @NAME
           , @DATA)  
set @ID = SCOPE_IDENTITY()
END

или используя набор результатов, и получите значение с помощью SqlCommand.ExecuteScalar () или SqlCommand.ExecuteReader ().

CREATE PROCEDURE [InsertMyTable] 
  @NAME NVARCHAR(100)
, @DATA NVARCHAR(MAX)  
AS
BEGIN
INSERT INTO [MyTable]
           ([NAME]
           ,[DATA])
     VALUES
           ( @NAME
           , @DATA)  
SELECT SCOPE_IDENTITY() ID
END

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

...