从 C# 中的存储过程中获取返回值

Posted

技术标签:

【中文标题】从 C# 中的存储过程中获取返回值【英文标题】:Getting return value from stored procedure in C# 【发布时间】:2010-10-16 22:07:29 【问题描述】:

我有以下疑问:

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go

ALTER PROCEDURE [dbo].[Validate]
@a varchar(50),
@b varchar(50) output

AS

SET @Password = 
(SELECT Password
FROM dbo.tblUser
WHERE Login = @a)

RETURN @b
GO

这编译得很好。我想执行这个查询并获取返回值。我的代码如下:

  SqlConnection SqlConn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["MyLocalSQLServer"].ConnectionString.ToString());
        System.Data.SqlClient.SqlCommand sqlcomm = new System.Data.SqlClient.SqlCommand("Validate", SqlConn);

        string returnValue = string.Empty;

        try
        
            SqlConn.Open();
            sqlcomm.CommandType = CommandType.StoredProcedure;

            SqlParameter param = new SqlParameter("@a", SqlDbType.VarChar);
            param.Direction = ParameterDirection.Input;
            param.Value = Username;
            sqlcomm.Parameters.Add(param);



            SqlParameter retval = sqlcomm.Parameters.Add("@b", SqlDbType.VarChar);
            retval.Direction = ParameterDirection.ReturnValue;


            string retunvalue = (string)sqlcomm.Parameters["@b"].Value;

注意:异常处理减少以保持代码简短。每次我到达最后一行时,都会返回 null。这段代码有什么逻辑错误?

【问题讨论】:

【参考方案1】:

Mehrdad 提出了一些很好的观点,但我注意到的主要一点是您从不运行查询...

SqlParameter retval = sqlcomm.Parameters.Add("@b", SqlDbType.VarChar);
retval.Direction = ParameterDirection.ReturnValue;
sqlcomm.ExecuteNonQuery(); // MISSING
string retunvalue = (string)sqlcomm.Parameters["@b"].Value;

【讨论】:

在最后一行中,您能否将sqlcomm.Parameters["@b"] 替换为retval 你应该试试看。 我不是在问关于我正在编写的代码的问题,我是在努力帮助你为未来的读者改进你的答案。【参考方案2】:
retval.Direction = ParameterDirection.Output;

ParameterDirection.ReturnValue 应该用于过程的“返回值”,而不是输出参数。它获取SQL RETURN 语句返回的值(参数名为@RETURN_VALUE)。

你应该SET @b = something而不是RETURN @b

顺便说一句,返回值参数总是int,而不是字符串。

【讨论】:

【参考方案3】:

我在返回值方面遇到了很多麻烦,所以最后我只是选择了一些东西。

解决办法就是在最后选择结果,然后在你的函数中返回查询结果。

在我的情况下,我正在执行存在检查:

IF (EXISTS (SELECT RoleName FROM dbo.Roles WHERE @RoleName = RoleName)) 
    SELECT 1
ELSE
    SELECT 0

然后

using (SqlConnection cnn = new SqlConnection(ConnectionString))

    SqlCommand cmd = cnn.CreateCommand();
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandText = "RoleExists";
    return (int) cmd.ExecuteScalar()

你应该能够用字符串值而不是 int 来做同样的事情。

【讨论】:

【参考方案4】:

这是基于Joel's 和Mehrdad's 的答案:您永远不会将retval 的参数绑定到sqlcommand。你需要一个

sqlcomm.Parameters.Add(retval);

并确保您正在运行该命令

sqlcomm.ExecuteNonQuery();

我也不确定为什么你有 2 个返回值字符串(returnValueretunvalue)。

【讨论】:

【参考方案5】:

你说你的 SQL 编译得很好,但我得到:必须声明标量变量“@Password”。

您还试图从存储过程中返回一个 varchar (@b),但 SQL Server 存储过程只能返回整数。

当你运行程序时你会得到错误:

'将 varchar 值 'x' 转换为数据类型 int 时转换失败。'

【讨论】:

【参考方案6】:

这里有多个问题:

    这是不可能的。您正在尝试返回一个 varchar。已存储 过程返回值只能是整数表达式。看 官方返回文档: https://msdn.microsoft.com/en-us/library/ms174998.aspx。 您的sqlcomm 从未被执行。你必须打电话 sqlcomm.ExecuteNonQuery(); 以执行您的命令。

这是一个使用 OUTPUT 参数的解决方案。这是通过以下方式测试的:

Windows 服务器 2012 .NET v4.0.30319 C# 4.0
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[Validate]
    @a varchar(50),
    @b varchar(50) OUTPUT
AS
BEGIN
    DECLARE @b AS varchar(50) = (SELECT Password FROM dbo.tblUser WHERE Login = @a)
    SELECT @b;
END
SqlConnection SqlConn = ...
var sqlcomm = new SqlCommand("Validate", SqlConn);

string returnValue = string.Empty;

try

    SqlConn.Open();
    sqlcomm.CommandType = CommandType.StoredProcedure;

    SqlParameter param = new SqlParameter("@a", SqlDbType.VarChar);
    param.Direction = ParameterDirection.Input;
    param.Value = Username;
    sqlcomm.Parameters.Add(param);

    SqlParameter output = sqlcomm.Parameters.Add("@b", SqlDbType.VarChar);
    ouput.Direction = ParameterDirection.Output;

    sqlcomm.ExecuteNonQuery(); // This line was missing

    returnValue = output.Value.ToString();

    // ... the rest of code

 catch (SqlException ex) 
    throw ex;

【讨论】:

您可能需要将 varchar 的长度添加为 sqlcomm.Parameters.Add("@b", SqlDbType.VarChar,50); 这里 50 是长度。【参考方案7】:

当我们从没有选择语句的存储过程中返回一个值时。 我们需要使用“ParameterDirection.ReturnValue”和“ExecuteScalar”命令来获取值。

CREATE PROCEDURE IsEmailExists
    @Email NVARCHAR(20)
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for procedure here
    IF EXISTS(SELECT Email FROM Users where Email = @Email)
    BEGIN
        RETURN 0 
    END
    ELSE
    BEGIN
        RETURN 1
    END
END

在 C# 中

GetOutputParaByCommand("IsEmailExists")

public int GetOutputParaByCommand(string Command)
        
            object identity = 0;
            try
            
                mobj_SqlCommand.CommandText = Command;
                SqlParameter SQP = new SqlParameter("returnVal", SqlDbType.Int);
                SQP.Direction = ParameterDirection.ReturnValue;
                mobj_SqlCommand.Parameters.Add(SQP);
                mobj_SqlCommand.Connection = mobj_SqlConnection;
                mobj_SqlCommand.ExecuteScalar();
                identity = Convert.ToInt32(SQP.Value);
                CloseConnection();
            
            catch (Exception ex)
            

                CloseConnection();
            
            return Convert.ToInt32(identity);
        

我们使用上面的c#函数得到SP“IsEmailExists”的返回值。

【讨论】:

【参考方案8】:

这个SP看起来很奇怪。它不会修改传递给@b 的内容。在 SP 中没有任何地方可以为 @b 分配任何东西。而且@Password 没有定义,所以这个SP 根本不起作用。

我猜你实际上想返回@Password,或者让 SET @b = (SELECT...)

如果您将 SP 修改为(注意,没有 OUTPUT 参数)会简单得多:

set ANSI_NULLS ON set QUOTED_IDENTIFIER ON go

ALTER PROCEDURE [dbo].[Validate] @a varchar(50)

AS

SELECT TOP 1 Password FROM dbo.tblUser WHERE Login = @a

然后,您的代码可以使用 cmd.ExecuteScalar,并接收结果。

【讨论】:

【参考方案9】:

对此有两点需要解决。首先设置存储过程将值存储在输出(而不是返回)参数中。

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go

ALTER PROCEDURE [dbo].[Validate]
@a varchar(50),
@b varchar(50) output

AS

SET @b = 
(SELECT Password
FROM dbo.tblUser
WHERE Login = @a)

RETURN
GO

这会将密码输入@b,您将获得它作为返回参数。然后在你的 C# 中得到它:

SqlConnection SqlConn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["MyLocalSQLServer"].ConnectionString.ToString());
    System.Data.SqlClient.SqlCommand sqlcomm = new System.Data.SqlClient.SqlCommand("Validate", SqlConn);

    string returnValue = string.Empty;

    try
    
        SqlConn.Open();
        sqlcomm.CommandType = CommandType.StoredProcedure;

        SqlParameter param = new SqlParameter("@a", SqlDbType.VarChar, 50);
        param.Direction = ParameterDirection.Input;
        param.Value = Username;
        sqlcomm.Parameters.Add(param);



        SqlParameter retval = new SqlParameter("@b", SqlDbType.VarChar, 50);
        retval.Direction = ParameterDirection.ReturnValue;
        sqlcomm.Parameters.Add(retval);

        sqlcomm.ExecuteNonQuery();
        SqlConn.Close();

        string retunvalue = retval.Value.ToString();
     

【讨论】:

我觉得应该是"retval.Direction = ParameterDirection.Output"【参考方案10】:

也许这会有所帮助。

数据库脚本:

USE [edata]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO


CREATE PROCEDURE [dbo].[InsertNewUser](
 @neuname NVARCHAR(255),
 @neupassword NVARCHAR(255),
 @neuposition NVARCHAR(255)
 )

AS

BEGIN 

BEGIN TRY

 DECLARE @check INT;

 SET @check = (SELECT count(eid) FROM eusers WHERE euname = @neuname);

IF(@check = 0)

INSERT INTO  eusers(euname,eupassword,eposition)
VALUES(@neuname,@neupassword,@neuposition);

DECLARE @lastid INT;

SET @lastid = @@IDENTITY;

RETURN @lastid;


END TRY


BEGIN CATCH

SELECT ERROR_LINE() as errline,
       ERROR_MESSAGE() as errmessage,
       ERROR_SEVERITY() as errsevirity

END CATCH

END

应用配置文件:

<?xml version="1.0" encoding="utf-8"?>
<configuration>

  <appSettings>
    <add key="conStr" value="Data Source=User\SQLEXPRESS;Initial Catalog=edata;Integrated Security=True"/>
  </appSettings>
</configuration>

数据访问层 (DAL):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
namespace DAL

    public static class DAL
    
        public static SqlConnection conn;

        static DAL()
        


            conn = new SqlConnection(ConfigurationManager.AppSettings["conStr"].ToString());
            conn.Open();


        


    

业务逻辑层(BLL):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
using DAL;
namespace BLL

    public static class BLL
    


        public static int InsertUser(string lastid, params SqlParameter[] coll)
        

            int lastInserted = 0;

            try
            


                SqlCommand comm = new SqlCommand();

                comm.Connection = DAL.DAL.conn;


                foreach (var param in coll)
                

                    comm.Parameters.Add(param);

                

                SqlParameter lastID = new SqlParameter();
                lastID.ParameterName = lastid;
                lastID.SqlDbType = SqlDbType.Int;
                lastID.Direction = ParameterDirection.ReturnValue;

                comm.Parameters.Add(lastID);

                comm.CommandType = CommandType.StoredProcedure;

                comm.CommandText = "InsertNewUser";

                comm.ExecuteNonQuery();

                lastInserted = (int)comm.Parameters[lastid].Value;

            

            catch (SqlException ex)
            


            

            finally 

                if (DAL.DAL.conn.State != ConnectionState.Closed) 

                    DAL.DAL.conn.Close();
                

                       

            return lastInserted;

        

    

实施:

BLL.BLL.InsertUser("@lastid",new SqlParameter("neuname","Ded"),
                 new SqlParameter("neupassword","Moro$ilka"),
                 new SqlParameter("neuposition","Moroz")
                 );

【讨论】:

【参考方案11】:

您混淆了返回值和输出变量的概念。 1- 输出变量:

Database----->:
create proc MySP
@a varchar(50),
@b varchar(50) output
AS
SET @Password = 
(SELECT Password
FROM dbo.tblUser
WHERE Login = @a)

C# ----->:

SqlConn.Open();
sqlcomm.CommandType = CommandType.StoredProcedure;

SqlParameter param = new SqlParameter("@a", SqlDbType.VarChar);
param.Direction = ParameterDirection.Input;//This is optional because Input is the default

param.Value = Username;
sqlcomm.Parameters.Add(param);

SqlParameter outputval = sqlcomm.Parameters.Add("@b", SqlDbType.VarChar);
outputval .Direction = ParameterDirection.Output//NOT ReturnValue;


string outputvalue = sqlcomm.Parameters["@b"].Value.ToString();

【讨论】:

我喜欢这个,但似乎“SET @Password”应该是“SET @b”【参考方案12】:

假设您需要将UsernamePassword传递给Stored Procedure,并知道登录是否成功并检查Stored Procedure中是否出现错误.

public bool IsLoginSuccess(string userName, string password)

    try
    
        SqlConnection SQLCon = new SqlConnection(WebConfigurationManager.ConnectionStrings["SqlConnector"].ConnectionString);
        SqlCommand sqlcomm = new SqlCommand();
        SQLCon.Open();
        sqlcomm.CommandType = CommandType.StoredProcedure;
        sqlcomm.CommandText = "spLoginCheck"; // Stored Procedure name
        sqlcomm.Parameters.AddWithValue("@Username", userName); // Input parameters
        sqlcomm.Parameters.AddWithValue("@Password", password); // Input parameters

        // Your output parameter in Stored Procedure           
        var returnParam1 = new SqlParameter
        
            ParameterName = "@LoginStatus",
            Direction = ParameterDirection.Output,
            Size = 1                    
        ;
        sqlcomm.Parameters.Add(returnParam1);

        // Your output parameter in Stored Procedure  
        var returnParam2 = new SqlParameter
        
            ParameterName = "@Error",
            Direction = ParameterDirection.Output,
            Size = 1000                    
        ;

        sqlcomm.Parameters.Add(returnParam2);

        sqlcomm.ExecuteNonQuery(); 
        string error = (string)sqlcomm.Parameters["@Error"].Value;
        string retunvalue = (string)sqlcomm.Parameters["@LoginStatus"].Value;                    
    
    catch (Exception ex)
    

    
    return false;

Web.Config 中的连接字符串

<connectionStrings>
    <add name="SqlConnector"
         connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;Initial Catalog=Databasename;User id=yourusername;Password=yourpassword"
         providerName="System.Data.SqlClient" />
  </connectionStrings>

这里是存储过程供参考

CREATE PROCEDURE spLoginCheck
    @Username Varchar(100),
    @Password Varchar(100) ,
    @LoginStatus char(1) = null output,
    @Error Varchar(1000) output 
AS
BEGIN

    SET NOCOUNT ON;
    BEGIN TRY
        BEGIN

            SET @Error = 'None'
            SET @LoginStatus = ''

            IF EXISTS(SELECT TOP 1 * FROM EMP_MASTER WHERE EMPNAME=@Username AND EMPPASSWORD=@Password)
            BEGIN
                SET @LoginStatus='Y'
            END

            ELSE
            BEGIN
                SET @LoginStatus='N'
            END

        END
    END TRY

    BEGIN CATCH
        BEGIN           
            SET @Error = ERROR_MESSAGE()
        END
    END CATCH
END
GO

【讨论】:

【参考方案13】:

当你使用时

cmd.Parameters.Add("@RETURN_VALUE", SqlDbType.Int).Direction = ParameterDirection.ReturnValue;

你必须确保你的存储过程有

return @RETURN_VALUE;

在存储过程结束时。

【讨论】:

【参考方案14】:

您尝试获取的值不是返回值,而是输出参数。您需要将参数方向更改为输出。

SqlParameter retval = sqlcomm.Parameters.Add("@b", SqlDbType.VarChar);
retval.Direction = ParameterDirection.Output;
command.ExecuteNonquery();
string retunvalue = (string)sqlcomm.Parameters["@b"].Value;

【讨论】:

【参考方案15】:

对于 .net core 3.0 和 dapper:

如果你的存储过程返回这个:

select ID, FILE_NAME from dbo.FileStorage where ID = (select max(ID) from dbo.FileStorage);

然后在c#中:

 var data = (_dbConnection.Query<FileUploadQueryResponse>
              ("dbo.insertFile", whateverParameters, commandType: CommandType.StoredProcedure)).ToList();
 var storedFileName = data[0].FILE_NAME;
 var id = data[0].ID;

如您所见,您可以定义一个简单的类来帮助从 dapper 的默认返回结构(我发现无法使用)中检索实际值:

public class FileUploadQueryResponse
  
    public string ID  get; set; 
    public string FILE_NAME  get; set; 
  

【讨论】:

【参考方案16】:

这行代码从 SQL Server 返回 Store StoredProcedure 返回值

cmd.Parameters.Add("@id", System.Data.SqlDbType.Int).Direction = System.Data.ParameterDirection.ReturnValue;                
cmd.ExecuteNonQuery();

Atfer 执行的查询值将从 SP 返回

id = (int)cmd.Parameters["@id"].Value;

【讨论】:

以上是关于从 C# 中的存储过程中获取返回值的主要内容,如果未能解决你的问题,请参考以下文章

Asp.net(C#) 获取 执行sql server 语句/存储过程后的 多个返回值?

C#获取存储过程的 Return返回值和Output输出参数值

在 C# 中如何获取存储过程的返回值 ?

C# 获取 oracle 存储过程的 返回值

如何从 ASP.NET Core Web API 中的 SQL Server 存储过程中获取返回值

如何从 Oracle 存储过程中获取两个返回值