【问题标题】:ADO.NET SqlCommand not returning correct value for SCOPE_IDENTITY()ADO.NET SqlCommand 没有为 SCOPE_IDENTITY() 返回正确的值
【发布时间】:2020-01-07 03:04:26
【问题描述】:

我有一张简单的桌子:

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

我有一个存储过程,它在这个表中插入一行并通过SCOPE_IDENTITY()返回PK 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

但是,当我使用 System.Data.SqlClient 在 C# 代码中执行类似的操作时,存储过程的返回值始终为 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

【问题讨论】:

  • 您的存储过程将标识值作为结果集返回,您将需要 c# 中的数据读取器来读取它。使用ExecuteReader

标签: sql-server ado.net sqlcommand scope-identity


【解决方案1】:

您的存储过程将 ID 作为存储过程返回值返回,该值应仅用于成功或失败,绝不能用于数据。

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 来使你拥有的东西起作用,但这是不好的做法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-26
    • 2019-05-05
    • 2019-03-21
    • 1970-01-01
    • 2014-07-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多