简体   繁体   English

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

[英]Getting return value from stored procedure in C#

I have the following query:我有以下查询:

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

This compiles perfectly fine.这编译得很好。 I want to execute this query and get the return value.我想执行这个查询并获取返回值。 My code is below:我的代码如下:

  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;

Note: Exception handling cut to keep the code short.注意:异常处理减少以保持代码简短。 Everytime I get to the last line, null is returned.每次我到达最后一行时,都会返回 null。 What's the logic error with this code?这段代码有什么逻辑错误?

Mehrdad makes some good points, but the main thing I noticed is that you never run the query ... Mehrdad 提出了一些很好的观点,但我注意到的主要一点是你从不运行查询......

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

ParameterDirection.ReturnValue should be used for the "return value" of the procedure, not output parameters. ParameterDirection.ReturnValue应该用于程序的“返回值”,而不是 output 参数。 It gets the value returned by the SQL RETURN statement (with the parameter named @RETURN_VALUE ).它获取 SQL RETURN语句返回的值(参数名为@RETURN_VALUE )。

Instead of RETURN @b you should SET @b = something而不是RETURN @b你应该SET @b = something

By the way, return value parameter is always int , not string.顺便说一句,返回值参数始终是int ,而不是 string。

I was having tons of trouble with the return value, so I ended up just selecting stuff at the end.我在返回值方面遇到了很多麻烦,所以最后我只是选择了一些东西。

The solution was just to select the result at the end and return the query result in your functinon.解决方案只是 select 最后的结果并在您的函数中返回查询结果。

In my case I was doing an exists check:就我而言,我正在执行存在检查:

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

Then然后

using (SqlConnection cnn = new SqlConnection(ConnectionString))
{
    SqlCommand cmd = cnn.CreateCommand();
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandText = "RoleExists";
    return (int) cmd.ExecuteScalar()
}

You should be able to do the same thing with a string value instead of an int.您应该能够使用字符串值而不是 int 来做同样的事情。

This is building on Joel's and Mehrdad's answers: you're never binding the parameter of the retval to the sqlcommand .这是建立在JoelMehrdad 的答案之上的:您永远不会将retval的参数绑定到sqlcommand You need a你需要一个

sqlcomm.Parameters.Add(retval);

and to make sure you're running the command并确保您正在运行命令

sqlcomm.ExecuteNonQuery();

I'm also not sure why you have 2 return value strings ( returnValue and retunvalue ).我也不确定为什么你有 2 个返回值字符串( returnValueretunvalue )。

There are multiple problems here:这里有多个问题:

  1. It is not possible.这不可能。 You are trying to return a varchar.您正在尝试返回一个 varchar。 Stored procedure return values can only be integer expressions.存储过程返回值只能是 integer 表达式。 See official RETURN documentation: https://msdn.microsoft.com/en-us/library/ms174998.aspx .请参阅官方 RETURN 文档: https://msdn.microsoft.com/en-us/library/ms174998.aspx
  2. Your sqlcomm was never executed.您的sqlcomm从未执行过。 You have to call sqlcomm.ExecuteNonQuery();你必须调用sqlcomm.ExecuteNonQuery(); in order to execute your command.为了执行你的命令。

Here is a solution using OUTPUT parameters.这是使用 OUTPUT 参数的解决方案。 This was tested with:对此进行了测试:

  • Windows Server 2012 Windows 服务器 2012
  • .NET v4.0.30319 .NET v4.0.30319
  • C# 4.0 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;
}

When we return a value from Stored procedure without select statement.当我们从没有 select 语句的存储过程中返回一个值时。 We need to use "ParameterDirection.ReturnValue" and "ExecuteScalar" command to get the value.我们需要使用“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

in C#在 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);
        }

We get the returned value of SP "IsEmailExists" using above c# function.我们使用上面的 c# function 得到 SP "IsEmailExists" 的返回值。

You say your SQL compiles fine, but I get: Must declare the scalar variable "@Password".你说你的 SQL 编译得很好,但我得到:必须声明标量变量“@Password”。

Also you are trying to return a varchar (@b) from your stored procedure, but SQL Server stored procedures can only return integers.您还试图从存储过程中返回一个 varchar (@b),但 SQL 服务器存储过程只能返回整数。

When you run the procedure you are going to get the error:当您运行该过程时,您将收到错误:

'Conversion failed when converting the varchar value 'x' to data type int.' '将 varchar 值 'x' 转换为数据类型 int 时转换失败。'

There are two things to fix about this.有两件事要解决这个问题。 First set up the stored procedure to store the value in the output ( not return ) parameter.首先设置存储过程存储output(不返回)参数中的值。

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

This will but the password into @b and you will get it as a return parameter.这会将密码输入@b,您将获得它作为返回参数。 Then to get it in your C# do this:然后将其放入您的 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();
     }

May be this will help.可能这会有所帮助。

Database script:数据库脚本:

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

Application configuration file:应用程序配置文件:

<?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>

Data Access Layer (DAL):数据访问层 (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();


        }


    }
}

Business Logic Layer(BLL):业务逻辑层(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;

        }

    }
}

Implementation:执行:

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

This SP looks very strange.这个SP看起来很奇怪。 It does not modify what is passed to @b.它不会修改传递给@b 的内容。 And nowhere in the SP you assign anything to @b.在 SP 中没有任何地方可以为 @b 分配任何东西。 And @Password is not defined, so this SP will not work at all.并且@Password 没有定义,所以这个SP 根本不起作用。

I would guess you actually want to return @Password, or to have SET @b = (SELECT...)我猜你实际上想要返回@Password,或者让 SET @b = (SELECT...)

Much simpler will be if you modify your SP to (note, no OUTPUT parameter):如果您将 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

Then, your code can use cmd.ExecuteScalar, and receive the result.然后,您的代码可以使用 cmd.ExecuteScalar,并接收结果。

You have mixed up the concept of the Return Value and Output variable.您混淆了 Return Value 和 Output 变量的概念。 1- Output Variable: 1- Output 变量:

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();

Suppose you need to pass Username and Password to Stored Procedure and know whether login is successful or not and check if any error has occurred in Stored Procedure .假设您需要将UsernamePassword传递给存储过程,并知道登录是否成功,并检查存储过程中是否发生任何错误。

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;
}

Your connection string in Web.Config 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>

And here is the Stored Procedure for reference这是存储过程供参考

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

When you use当你使用

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

you must then ensure your stored procedure has然后,您必须确保您的存储过程具有

return @RETURN_VALUE;

at the end of the stored procedure.在存储过程结束时。

The value you are trying to get is not a return value but an output parameter.您尝试获取的值不是返回值,而是 output 参数。 You need to change parametere direction to Output.您需要将参数方向更改为 Output。

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

For .net core 3.0 and dapper:对于 .net 核心 3.0 和 dapper:

If your stored procedure returns this:如果您的存储过程返回此:

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

Then in c#:然后在 c# 中:

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

As you can see, you can define a simple class to help with retrieving the actual values from dapper's default return structure (which I found impossible to work with):如您所见,您可以定义一个简单的 class 来帮助从 dapper 的默认返回结构(我发现无法使用)中检索实际值:

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

This Line of code returns Store StoredProcedure returned value from SQL Server这行代码从 SQL 服务器返回 Store StoredProcedure 返回值

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

Atfer Execution of query value will returned from SP Atfer 执行查询值将从 SP 返回

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM