在C#中使用存储过程输出参数


69

我在将来自Sql Server存储过程的输出参数返回到C#变量时遇到问题。我不仅在这里而且在其他站点上也阅读了与此相关的其他文章,但我无法使其正常工作。这是我目前拥有的。目前,我只是想打印返回的值。以下代码返回空值。我试图返回的是主键。我已经尝试使用@@IDENTITYSCOPE_INDENTITY()(即SET @NewId = SCOPE_IDENTITY())。

存储过程:

CREATE PROCEDURE usp_InsertContract
    @ContractNumber varchar(7),

    @NewId int OUTPUT
AS
BEGIN

    INSERT into [dbo].[Contracts] (ContractNumber)
        VALUES (@ContractNumber)

    Select @NewId = Id From [dbo].[Contracts] where ContractNumber = @ContractNumber
END

打开数据库:

pvConnectionString = "Server = Desktop-PC\\SQLEXPRESS; Database = PVDatabase; User ID = sa;
    PASSWORD = *******; Trusted_Connection = True;";

try
{
    pvConnection = new SqlConnection(pvConnectionString);
    pvConnection.Open();
}
catch (Exception e)
{
    databaseError = true;
}

执行命令:

pvCommand = new SqlCommand("usp_InsertContract", pvConnection);

pvCommand.Transaction = pvTransaction;
pvCommand.CommandType = CommandType.StoredProcedure;    

pvCommand.Parameters.Clear();
pvCommand.Parameters.Add(new SqlParameter("@ContractNumber", contractNumber));

SqlParameter pvNewId = new SqlParameter();
pvNewId.ParameterName = "@NewId";
pvNewId.DbType = DbType.Int32;
pvNewId.Direction = ParameterDirection.Output;
pvCommand.Parameters.Add(pvNewId);

try
{
    sqlRows = pvCommand.ExecuteNonQuery();

    if (sqlRows > 0)
        Debug.Print("New Id Inserted =  ", 
            pvCommand.Parameters["@NewId"].Value.ToString()); 
    }
    catch (Exception e)
    {
        Debug.Print("Insert Exception Type: {0}", e.GetType());
        Debug.Print("  Message: {0}", e.Message);
    }
}

1
我还将使用SCOPE_IDENTITY()而不是重新查询同一张表。并且,当分配给单个输出变量时,如果出现“查询可以返回多个结果”错误。
JMC 2012年

我是否使用set命令来执行此操作?SET NewId = SCOPE_IDENTITY()?我将select语句替换为以下内容... SET @NewId = SCOPE_IDENTITY(),但仍返回空值。
加里

是的,我可以在数据库中看到该行。
加里

我已经在代码中添加了连接的打开和命令的创建。
加里

1
您可以在读取输出参数之前尝试提交事务吗?
史蒂夫

Answers:


139

我稍微修改了您的存储过程(使用SCOPE_IDENTITY),它看起来像这样:

CREATE PROCEDURE usp_InsertContract
    @ContractNumber varchar(7),
    @NewId int OUTPUT
AS
BEGIN
    INSERT INTO [dbo].[Contracts] (ContractNumber)
    VALUES (@ContractNumber)

    SELECT @NewId = SCOPE_IDENTITY()
END

我试过了,它工作得很好(使用修改后的存储过程):

// define connection and command, in using blocks to ensure disposal
using(SqlConnection conn = new SqlConnection(pvConnectionString ))
using(SqlCommand cmd = new SqlCommand("dbo.usp_InsertContract", conn))
{
    cmd.CommandType = CommandType.StoredProcedure;

    // set up the parameters
    cmd.Parameters.Add("@ContractNumber", SqlDbType.VarChar, 7);
    cmd.Parameters.Add("@NewId", SqlDbType.Int).Direction = ParameterDirection.Output;

    // set parameter values
    cmd.Parameters["@ContractNumber"].Value = contractNumber;

    // open connection and execute stored procedure
    conn.Open();
    cmd.ExecuteNonQuery();

    // read output value from @NewId
    int contractID = Convert.ToInt32(cmd.Parameters["@NewId"].Value);
    conn.Close();
}

这在您的环境中也有效吗?我不能说为什么您的原始代码不起作用-但是当我在这里执行VS2010和SQL Server 2008 R2时,它可以完美地工作。

如果您没有获取值-那么我怀疑您的表Contracts可能没有真正包含其IDENTITY属性的列。


8
谢谢!感谢您拨冗为我们那些在数年后使用Google进行搜索的人付出的时间。:-)
John Suit

@marc_s如果Varchar输出的大小未知,那么如何解决该问题。
Gunner

cmd.Parameters.Add(“ @ NewId”,SqlDbType.Int).Direction = ParameterDirection.Output; 需要更改为cmd.Parameters.AddWithValue(“ @ NewId”,SqlDbType.Int).Direction = ParameterDirection.Output; 这是因为它已贬值
user3569147

4
@ user3569147:绝对不是!您应该检查一下是否可以停止使用AddWithValue()了吗?并停止使用.AddWithValue()-这可能会导致意外和令人惊讶的结果...
marc_s

3

在更改存储过程之前,请检查当前存储过程的输出是什么。在SQL Server管理中,运行以下命令:

DECLARE @NewId int
EXEC    @return_value = [dbo].[usp_InsertContract]
            N'Gary',
            @NewId OUTPUT
SELECT  @NewId

看看它返回什么。这可能会给您一些提示,说明为什么未填写输出参数。


这只是作为答案,但可能应该是评论,但是由于使用多个@字符(即使mod被拒绝是正确的),我也无法将其转换,并且我也不必费心对其进行编辑以使其起作用。:)
Kev 2012年

1

存储过程.........

CREATE PROCEDURE usp_InsertContract
    @ContractNumber varchar(7)
AS
BEGIN

    INSERT into [dbo].[Contracts] (ContractNumber)
        VALUES (@ContractNumber)

    SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY]
END

C#

pvCommand.CommandType = CommandType.StoredProcedure;

pvCommand.Parameters.Clear();
pvCommand.Parameters.Add(new SqlParameter("@ContractNumber", contractNumber));
object uniqueId;
int id;
    try
    {
    uniqueId = pvCommand.ExecuteScalar();
     id = Convert.ToInt32(uniqueId);
    }
    catch (Exception e)
    {
        Debug.Print("  Message: {0}", e.Message);
    }
}

编辑: “我仍然返回DBNull值。...对象不能从DBNull强制转换为其他类型。明天我会再处理这个。我要去做其他工作,”

我相信您的SQL表中的Id列不是身份列。

在此处输入图片说明


但这不能回答为什么新id的输出参数返回null的问题。使用,ExecuteScalar您可以检索返回值而不是输出参数。
Tim Schmelter 2012年

进行此更改时,我还会收到以下错误...对象引用未设置为对象的实例。
加里2012年

那是因为在SQL中,该参数称为SCOPE_IDENTITY;C#仍在寻找一个名为@ContractNumber的参数。
Russ Clarke

1
ContractNumber是要添加到表中的值。主键称为ID。
加里

ExecuteScalar返回一个对象,而不是一个int。我在执行代码时收到异常。
加里

0

在您的C#代码中,您正在为该命令使用事务。只需提交事务,然后访问您的参数值,即可获得该值。为我工作。:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.