Rückgabewert von gespeicherter Prozedur in C # abrufen

71

Ich habe folgende Frage:

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

Dies kompiliert perfekt. Ich möchte diese Abfrage ausführen und den Rückgabewert erhalten. Mein Code ist unten:

  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;

Hinweis: Die Ausnahmebehandlung wurde abgeschnitten, um den Code kurz zu halten. Jedes Mal, wenn ich zur letzten Zeile komme, wird null zurückgegeben. Was ist der logische Fehler bei diesem Code?

GurdeepS
quelle

Antworten:

95

Mehrdad macht einige gute Punkte, aber die Hauptsache, die mir aufgefallen ist, ist, dass Sie die Abfrage nie ausführen ...

SqlParameter retval = sqlcomm.Parameters.Add("@b", SqlDbType.VarChar);
retval.Direction = ParameterDirection.ReturnValue;
sqlcomm.ExecuteNonQuery(); // MISSING
string retunvalue = (string)sqlcomm.Parameters["@b"].Value;
Joel Coehoorn
quelle
2
In der letzten Zeile haben, könnten Sie ersetzt sqlcomm.Parameters["@b"]mit retval?
Tab Alleman
1
Sie sollten es versuchen, um zu sehen.
Joel Coehoorn
5
Ich stelle keine Frage zu Code, an dem ich arbeite. Ich versuche Ihnen zu helfen, Ihre Antwort für zukünftige Leser zu verbessern.
Tab Alleman
63
retval.Direction = ParameterDirection.Output;

ParameterDirection.ReturnValuesollte für den "Rückgabewert" der Prozedur verwendet werden, nicht für Ausgabeparameter. Es erhält den von der SQL- RETURNAnweisung zurückgegebenen Wert (mit dem benannten Parameter @RETURN_VALUE).

Anstelle von RETURN @bdir sollteSET @b = something

Der Rückgabewertparameter ist übrigens immer ein intString.

mmx
quelle
11

Ich hatte jede Menge Probleme mit dem Rückgabewert, also habe ich am Ende nur Sachen ausgewählt.

Die Lösung bestand darin, das Ergebnis am Ende auszuwählen und das Abfrageergebnis in Ihrer Funktion zurückzugeben.

In meinem Fall habe ich eine Existenzprüfung durchgeführt:

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

Dann

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

Sie sollten in der Lage sein, dasselbe mit einem Zeichenfolgenwert anstelle eines int zu tun.

Alan Jackson
quelle
5

Dies baut auf den Antworten von Joel und Mehrdad auf: Sie binden niemals den Parameter des retvalan das sqlcommand. Du brauchst ein

sqlcomm.Parameters.Add(retval);

und um sicherzustellen, dass Sie den Befehl ausführen

sqlcomm.ExecuteNonQuery();

Ich bin mir auch nicht sicher, warum Sie 2 Rückgabewertzeichenfolgen ( returnValueund retunvalue) haben.

Manatherin
quelle
4

Sie sagen, Ihr SQL wird gut kompiliert, aber ich verstehe: Muss die skalare Variable "@Password" deklarieren.

Außerdem versuchen Sie, ein varchar (@b) von Ihrer gespeicherten Prozedur zurückzugeben, aber gespeicherte SQL Server-Prozeduren können nur Ganzzahlen zurückgeben.

Wenn Sie die Prozedur ausführen, wird folgende Fehlermeldung angezeigt:

'Die Konvertierung ist fehlgeschlagen, wenn der Varchar-Wert' x 'in den Datentyp int konvertiert wurde.'

Martin Brown
quelle
4

Wenn wir einen Wert aus der gespeicherten Prozedur ohne select-Anweisung zurückgeben. Wir müssen die Befehle "ParameterDirection.ReturnValue" und "ExecuteScalar" verwenden, um den Wert abzurufen.

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 #

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

Wir erhalten den zurückgegebenen Wert von SP "IsEmailExists" unter Verwendung der obigen c # -Funktion.

Ravindra Vairagi
quelle
3

Dieser SP sieht sehr seltsam aus. Es wird nicht geändert, was an @b übergeben wird. Und nirgends im SP weisen Sie @b etwas zu. Und @Password ist nicht definiert, sodass dieser SP überhaupt nicht funktioniert.

Ich würde vermuten, dass Sie tatsächlich @Password zurückgeben oder SET @b = (SELECT ...) haben möchten.

Viel einfacher ist es, wenn Sie Ihren SP auf ändern (Hinweis, kein OUTPUT-Parameter):

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

Dann kann Ihr Code cmd.ExecuteScalar verwenden und das Ergebnis erhalten.

Sunny Milenov
quelle
3

Es gibt zwei Dinge zu beheben. Richten Sie zuerst die gespeicherte Prozedur ein, um den Wert im Ausgabeparameter (nicht Rückgabeparameter) zu speichern.

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

Dies wird aber das Passwort in @b und Sie erhalten es als Rückgabeparameter. Um es in Ihr C # zu bekommen, gehen Sie wie folgt vor:

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();
     }
Meilen
quelle
Ich denke, dass dies "retval.Direction = ParameterDirection.Output" sein sollte
daveywc
3

Vielleicht hilft das.

Datenbankskript:

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

Anwendungskonfigurationsdatei:

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

Datenzugriffsschicht (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):

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;

        }

    }
}

Implementierung :

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

Hier gibt es mehrere Probleme:

  1. Es ist nicht möglich. Sie versuchen, einen Varchar zurückzugeben. Rückgabewerte für gespeicherte Prozeduren können nur ganzzahlige Ausdrücke sein. Siehe offizielle RETURN-Dokumentation: https://msdn.microsoft.com/en-us/library/ms174998.aspx .
  2. Ihr sqlcommwurde nie hingerichtet. Sie müssen aufrufen sqlcomm.ExecuteNonQuery();, um Ihren Befehl auszuführen.

Hier ist eine Lösung mit OUTPUT-Parametern. Dies wurde getestet mit:

  • Windows Server 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;
}
Djuka
quelle
Möglicherweise müssen Sie die Länge von varchar hinzufügen, da sqlcomm.Parameters.Add("@b", SqlDbType.VarChar,50);hier 50 die Länge ist.
Abdul Qayyum
2

Sie haben das Konzept der Rückgabewert- und Ausgabevariablen verwechselt. 1- Ausgangsvariable:

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();
Mehran Sarrafi
quelle
Ich mag das, aber es scheint, dass "SET @Password" "SET @b" sein sollte
Mike K
1

Angenommen , Sie müssen passieren Usernameund Passwordzu gespeicherten Prozedur und wissen , ob die Anmeldung erfolgreich ist oder nicht , und prüfen Sie, ob ein Fehler in aufgetreten ist 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;
}

Ihre Verbindungszeichenfolge in 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>

Und hier ist die gespeicherte Prozedur als Referenz

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
Sarath KS
quelle
1

Wenn Sie verwenden

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

Sie müssen dann sicherstellen, dass Ihre gespeicherte Prozedur hat

return @RETURN_VALUE;

am Ende der gespeicherten Prozedur.

jbooker
quelle
0

Der Wert, den Sie abrufen möchten, ist kein Rückgabewert, sondern ein Ausgabeparameter. Sie müssen die Parameterrichtung in Ausgabe ändern.

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

Für .net Core 3.0 und Dapper:

Wenn Ihre gespeicherte Prozedur Folgendes zurückgibt:

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

Dann in c #:

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

Wie Sie sehen können, können Sie eine einfache Klasse definieren, um die tatsächlichen Werte aus der Standard-Rückgabestruktur von dapper abzurufen (mit der ich nicht arbeiten konnte):

public class FileUploadQueryResponse
  {
    public string ID { get; set; }
    public string FILE_NAME { get; set; }
  }
Dean
quelle
-1

Diese Codezeile gibt den von SQL Server zurückgegebenen Wert von Store StoredProcedure zurück

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

Atfer Die Ausführung des Abfragewerts wird von SP zurückgegeben

id = (int)cmd.Parameters["@id"].Value;
Umar Farooq Ansari
quelle